@swarmclawai/swarmclaw 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/bin/daemon-cmd.js +169 -0
- package/bin/server-cmd.js +3 -0
- package/bin/swarmclaw.js +11 -0
- package/package.json +17 -16
- package/src/app/api/agents/[id]/clone/route.ts +3 -32
- package/src/app/api/agents/[id]/route.ts +6 -158
- package/src/app/api/agents/[id]/status/route.ts +2 -3
- package/src/app/api/agents/[id]/thread/route.ts +4 -17
- package/src/app/api/agents/bulk/route.ts +5 -47
- package/src/app/api/agents/route.ts +5 -119
- package/src/app/api/agents/trash/route.ts +13 -24
- package/src/app/api/auth/route.ts +3 -9
- package/src/app/api/autonomy/estop/route.ts +5 -5
- package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
- package/src/app/api/chatrooms/[id]/route.ts +23 -2
- package/src/app/api/chatrooms/route.ts +13 -2
- package/src/app/api/chats/[id]/clear/route.ts +2 -13
- package/src/app/api/chats/[id]/deploy/route.ts +2 -3
- package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
- package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
- package/src/app/api/chats/[id]/queue/route.ts +17 -64
- package/src/app/api/chats/[id]/retry/route.ts +4 -22
- package/src/app/api/chats/[id]/route.ts +10 -138
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/migrate-messages/route.ts +7 -0
- package/src/app/api/chats/route.ts +13 -134
- package/src/app/api/connectors/[id]/access/route.ts +12 -229
- package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
- package/src/app/api/connectors/[id]/health/route.ts +12 -39
- package/src/app/api/connectors/[id]/route.ts +14 -122
- package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
- package/src/app/api/connectors/doctor/route.ts +1 -1
- package/src/app/api/connectors/route.ts +12 -70
- package/src/app/api/credentials/[id]/route.ts +2 -4
- package/src/app/api/credentials/route.ts +10 -19
- package/src/app/api/daemon/health-check/route.ts +3 -4
- package/src/app/api/daemon/route.ts +10 -8
- package/src/app/api/documents/route.ts +11 -10
- package/src/app/api/external-agents/route.ts +3 -3
- package/src/app/api/gateways/[id]/health/route.ts +2 -3
- package/src/app/api/gateways/[id]/route.ts +7 -122
- package/src/app/api/gateways/route.ts +3 -103
- package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
- package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
- package/src/app/api/openclaw/directory/route.ts +2 -2
- package/src/app/api/openclaw/history/route.ts +3 -5
- package/src/app/api/providers/[id]/route.test.ts +49 -0
- package/src/app/api/providers/ollama/route.ts +6 -5
- package/src/app/api/schedules/[id]/route.ts +14 -108
- package/src/app/api/schedules/[id]/run/route.ts +6 -67
- package/src/app/api/schedules/route.ts +9 -51
- package/src/app/api/settings/route.ts +4 -3
- package/src/app/api/setup/check-provider/route.ts +23 -1
- package/src/app/api/setup/openclaw-device/route.ts +2 -2
- package/src/app/api/system/status/route.ts +2 -2
- package/src/app/api/tasks/[id]/route.ts +16 -202
- package/src/app/api/tasks/bulk/route.ts +5 -86
- package/src/app/api/tasks/metrics/route.ts +2 -1
- package/src/app/api/tasks/route.ts +11 -171
- package/src/app/api/upload/route.ts +1 -1
- package/src/app/api/uploads/[filename]/route.ts +1 -1
- package/src/app/api/uploads/route.ts +1 -1
- package/src/app/api/webhooks/[id]/history/route.ts +2 -2
- package/src/app/layout.tsx +9 -6
- package/src/app/protocols/page.tsx +71 -89
- package/src/app/tasks/page.tsx +32 -32
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-sheet.tsx +5 -5
- package/src/components/auth/setup-wizard/index.tsx +4 -4
- package/src/components/auth/setup-wizard/step-agents.tsx +1 -1
- package/src/components/auth/setup-wizard/step-connect.tsx +1 -1
- package/src/components/auth/setup-wizard/utils.ts +1 -1
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
- package/src/components/connectors/connector-list.tsx +26 -40
- package/src/components/connectors/connector-sheet.tsx +95 -149
- package/src/components/gateways/gateway-sheet.tsx +61 -110
- package/src/components/layout/live-query-sync.tsx +121 -0
- package/src/components/protocols/structured-session-launcher.tsx +24 -45
- package/src/components/providers/app-query-provider.tsx +17 -0
- package/src/components/providers/provider-list.tsx +60 -61
- package/src/components/providers/provider-sheet.tsx +74 -56
- package/src/components/skills/skill-list.tsx +5 -18
- package/src/components/skills/skill-sheet.tsx +21 -20
- package/src/components/skills/skills-workspace.tsx +48 -87
- package/src/components/tasks/task-card.tsx +20 -13
- package/src/components/tasks/task-column.tsx +22 -7
- package/src/components/tasks/task-list.tsx +8 -11
- package/src/components/tasks/task-sheet.tsx +111 -103
- package/src/features/agents/queries.ts +20 -0
- package/src/features/chatrooms/queries.ts +20 -0
- package/src/features/chats/queries.ts +27 -0
- package/src/features/connectors/queries.ts +145 -0
- package/src/features/credentials/queries.ts +37 -0
- package/src/features/extensions/queries.ts +26 -0
- package/src/features/external-agents/queries.ts +36 -0
- package/src/features/gateways/queries.ts +274 -0
- package/src/features/missions/queries.ts +23 -0
- package/src/features/projects/queries.ts +20 -0
- package/src/features/protocols/queries.ts +149 -0
- package/src/features/providers/queries.ts +142 -0
- package/src/features/settings/queries.ts +20 -0
- package/src/features/skills/queries.ts +182 -0
- package/src/features/tasks/queries.ts +189 -0
- package/src/hooks/use-ws.ts +3 -2
- package/src/lib/app/api-client.ts +2 -2
- package/src/lib/providers/index.test.ts +108 -0
- package/src/lib/providers/index.ts +38 -15
- package/src/lib/query/client.ts +17 -0
- package/src/lib/server/agents/agent-runtime-config.ts +1 -1
- package/src/lib/server/agents/agent-service.ts +429 -0
- package/src/lib/server/agents/agent-thread-session.ts +6 -5
- package/src/lib/server/agents/autonomy-contract.ts +1 -4
- package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
- package/src/lib/server/agents/delegation-advisory.ts +251 -0
- package/src/lib/server/agents/main-agent-loop.ts +98 -40
- package/src/lib/server/agents/subagent-runtime.ts +12 -0
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
- package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
- package/src/lib/server/build-llm.ts +7 -15
- package/src/lib/server/capability-router.test.ts +70 -1
- package/src/lib/server/capability-router.ts +24 -99
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
- package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
- package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
- package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
- package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
- package/src/lib/server/chat-execution/message-classifier.ts +74 -32
- package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
- package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
- package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
- package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
- package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
- package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
- package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
- package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
- package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
- package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
- package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
- package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
- package/src/lib/server/chats/chat-session-service.ts +410 -0
- package/src/lib/server/connectors/access.ts +1 -1
- package/src/lib/server/connectors/commands.ts +7 -6
- package/src/lib/server/connectors/connector-inbound.ts +14 -7
- package/src/lib/server/connectors/connector-outbound.ts +16 -11
- package/src/lib/server/connectors/connector-service.ts +453 -0
- package/src/lib/server/connectors/delivery.ts +17 -12
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
- package/src/lib/server/connectors/media.ts +1 -1
- package/src/lib/server/connectors/response-media.ts +1 -1
- package/src/lib/server/connectors/session-consolidation.ts +11 -7
- package/src/lib/server/connectors/session.ts +9 -7
- package/src/lib/server/connectors/voice-note.ts +2 -1
- package/src/lib/server/context-manager.ts +20 -1
- package/src/lib/server/cost.ts +2 -3
- package/src/lib/server/credentials/credential-repository.ts +43 -4
- package/src/lib/server/credentials/credential-service.ts +112 -0
- package/src/lib/server/daemon/admin-metadata.ts +64 -0
- package/src/lib/server/daemon/controller.ts +577 -0
- package/src/lib/server/daemon/daemon-runtime.ts +352 -0
- package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
- package/src/lib/server/daemon/types.ts +101 -0
- package/src/lib/server/embeddings.ts +3 -9
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -2
- package/src/lib/server/execution-brief.test.ts +167 -0
- package/src/lib/server/execution-brief.ts +295 -0
- package/src/lib/server/execution-engine/chat-turn.ts +9 -0
- package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
- package/src/lib/server/execution-engine/index.ts +35 -0
- package/src/lib/server/execution-engine/task-attempt.ts +303 -0
- package/src/lib/server/execution-engine/types.ts +33 -0
- package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
- package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
- package/src/lib/server/memory/session-archive-memory.ts +12 -10
- package/src/lib/server/messages/message-repository.ts +330 -0
- package/src/lib/server/missions/mission-service/core.ts +8 -6
- package/src/lib/server/openclaw/agent-resolver.ts +2 -3
- package/src/lib/server/openclaw/doctor.ts +1 -1
- package/src/lib/server/openclaw/gateway.test.ts +10 -1
- package/src/lib/server/openclaw/gateway.ts +5 -14
- package/src/lib/server/openclaw/health.ts +3 -11
- package/src/lib/server/openclaw/sync.ts +8 -6
- package/src/lib/server/persistence/storage-context.ts +3 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
- package/src/lib/server/protocols/protocol-normalization.ts +1 -1
- package/src/lib/server/protocols/protocol-queries.ts +13 -7
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
- package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
- package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
- package/src/lib/server/protocols/protocol-swarm.ts +8 -8
- package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
- package/src/lib/server/protocols/protocol-templates.ts +4 -2
- package/src/lib/server/protocols/protocol-types.ts +10 -7
- package/src/lib/server/provider-endpoint.ts +7 -12
- package/src/lib/server/provider-model-discovery.ts +2 -11
- package/src/lib/server/query-expansion.ts +5 -6
- package/src/lib/server/run-context.test.ts +365 -0
- package/src/lib/server/run-context.ts +367 -0
- package/src/lib/server/runtime/heartbeat-service.ts +7 -5
- package/src/lib/server/runtime/queue/core.ts +61 -190
- package/src/lib/server/runtime/run-ledger.ts +8 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
- package/src/lib/server/schedules/schedule-route-service.ts +230 -0
- package/src/lib/server/service-result.ts +16 -0
- package/src/lib/server/session-note.ts +2 -3
- package/src/lib/server/session-reset-policy.ts +4 -3
- package/src/lib/server/session-tools/connector.ts +9 -6
- package/src/lib/server/session-tools/context-mgmt.ts +58 -9
- package/src/lib/server/session-tools/crud.ts +162 -10
- package/src/lib/server/session-tools/delegate.ts +1 -1
- package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
- package/src/lib/server/session-tools/memory.ts +6 -4
- package/src/lib/server/session-tools/session-info.test.ts +56 -0
- package/src/lib/server/session-tools/session-info.ts +119 -12
- package/src/lib/server/session-tools/skill-runtime.ts +3 -1
- package/src/lib/server/session-tools/skills.ts +15 -15
- package/src/lib/server/session-tools/subagent.test.ts +115 -1
- package/src/lib/server/session-tools/subagent.ts +125 -7
- package/src/lib/server/session-tools/team-context.ts +4 -3
- package/src/lib/server/session-tools/wallet.ts +0 -58
- package/src/lib/server/sessions/session-lineage.ts +55 -0
- package/src/lib/server/sessions/session-repository.ts +2 -2
- package/src/lib/server/skills/learned-skills.ts +24 -23
- package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
- package/src/lib/server/skills/skill-repository.ts +136 -13
- package/src/lib/server/skills/skill-suggestions.ts +25 -28
- package/src/lib/server/storage-normalization.test.ts +44 -267
- package/src/lib/server/storage-normalization.ts +75 -0
- package/src/lib/server/storage.ts +19 -0
- package/src/lib/server/structured-extract.ts +3 -14
- package/src/lib/server/tasks/task-followups.ts +16 -11
- package/src/lib/server/tasks/task-result.test.ts +25 -29
- package/src/lib/server/tasks/task-result.ts +5 -9
- package/src/lib/server/tasks/task-route-service.ts +449 -0
- package/src/lib/server/text-normalization.ts +41 -0
- package/src/lib/server/tool-planning.ts +6 -42
- package/src/lib/server/upload-path.ts +5 -0
- package/src/lib/server/working-state/extraction.ts +614 -0
- package/src/lib/server/working-state/normalization.ts +866 -0
- package/src/lib/server/working-state/prompt.ts +60 -0
- package/src/lib/server/working-state/repository.ts +38 -0
- package/src/lib/server/working-state/service.test.ts +253 -0
- package/src/lib/server/working-state/service.ts +293 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/ws-client.ts +3 -3
- package/src/stores/slices/task-slice.ts +1 -4
- package/src/stores/use-chatroom-store.ts +2 -2
- package/src/types/index.ts +277 -12
package/src/app/tasks/page.tsx
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useCallback, useState, useRef, useMemo } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { useAgentsQuery } from '@/features/agents/queries'
|
|
6
|
+
import { useProjectsQuery } from '@/features/projects/queries'
|
|
7
|
+
import {
|
|
8
|
+
useBulkUpdateTasksMutation,
|
|
9
|
+
useImportGitHubIssuesMutation,
|
|
10
|
+
useTasksQuery,
|
|
11
|
+
useUpdateTaskMutation,
|
|
12
|
+
type GitHubIssueImportResult,
|
|
13
|
+
} from '@/features/tasks/queries'
|
|
7
14
|
import { TaskColumn } from '@/components/tasks/task-column'
|
|
8
15
|
import { TaskCard } from '@/components/tasks/task-card'
|
|
9
16
|
import { Skeleton } from '@/components/shared/skeleton'
|
|
@@ -63,18 +70,18 @@ function attentionRank(task: BoardTask, now: number | null): number {
|
|
|
63
70
|
|
|
64
71
|
export default function TasksPage() {
|
|
65
72
|
const now = useNow()
|
|
66
|
-
const tasks = useAppStore((s) => s.tasks)
|
|
67
|
-
const loadTasks = useAppStore((s) => s.loadTasks)
|
|
68
|
-
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
69
73
|
const setTaskSheetOpen = useAppStore((s) => s.setTaskSheetOpen)
|
|
70
74
|
const setEditingTaskId = useAppStore((s) => s.setEditingTaskId)
|
|
71
|
-
const agents = useAppStore((s) => s.agents)
|
|
72
|
-
const projects = useAppStore((s) => s.projects)
|
|
73
|
-
const loadProjects = useAppStore((s) => s.loadProjects)
|
|
74
75
|
const activeProjectFilter = useAppStore((s) => s.activeProjectFilter)
|
|
75
76
|
const setActiveProjectFilter = useAppStore((s) => s.setActiveProjectFilter)
|
|
76
77
|
const showArchived = useAppStore((s) => s.showArchivedTasks)
|
|
77
78
|
const setShowArchived = useAppStore((s) => s.setShowArchivedTasks)
|
|
79
|
+
const { data: tasks = {}, isLoading: tasksLoading } = useTasksQuery({ includeArchived: true })
|
|
80
|
+
const { data: agents = {}, isLoading: agentsLoading } = useAgentsQuery()
|
|
81
|
+
const { data: projects = {}, isLoading: projectsLoading } = useProjectsQuery()
|
|
82
|
+
const bulkUpdateTasksMutation = useBulkUpdateTasksMutation()
|
|
83
|
+
const updateTaskMutation = useUpdateTaskMutation()
|
|
84
|
+
const importGitHubIssuesMutation = useImportGitHubIssuesMutation()
|
|
78
85
|
|
|
79
86
|
// Bulk selection
|
|
80
87
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
|
|
@@ -97,8 +104,7 @@ export default function TasksPage() {
|
|
|
97
104
|
if (selectedIds.size === 0) return
|
|
98
105
|
setBulkActing(true)
|
|
99
106
|
try {
|
|
100
|
-
await
|
|
101
|
-
await loadTasks()
|
|
107
|
+
await bulkUpdateTasksMutation.mutateAsync({ ids: [...selectedIds], patch: { status } })
|
|
102
108
|
toast.success(`Moved ${selectedIds.size} task(s) to ${status}`)
|
|
103
109
|
clearSelection()
|
|
104
110
|
} catch {
|
|
@@ -106,15 +112,13 @@ export default function TasksPage() {
|
|
|
106
112
|
} finally {
|
|
107
113
|
setBulkActing(false)
|
|
108
114
|
}
|
|
109
|
-
|
|
110
|
-
}, [selectedIds])
|
|
115
|
+
}, [bulkUpdateTasksMutation, clearSelection, selectedIds])
|
|
111
116
|
|
|
112
117
|
const handleBulkAgent = useCallback(async (agentId: string) => {
|
|
113
118
|
if (selectedIds.size === 0) return
|
|
114
119
|
setBulkActing(true)
|
|
115
120
|
try {
|
|
116
|
-
await
|
|
117
|
-
await loadTasks()
|
|
121
|
+
await bulkUpdateTasksMutation.mutateAsync({ ids: [...selectedIds], patch: { agentId } })
|
|
118
122
|
const name = agents[agentId]?.name || 'agent'
|
|
119
123
|
toast.success(`Assigned ${selectedIds.size} task(s) to ${name}`)
|
|
120
124
|
clearSelection()
|
|
@@ -123,15 +127,13 @@ export default function TasksPage() {
|
|
|
123
127
|
} finally {
|
|
124
128
|
setBulkActing(false)
|
|
125
129
|
}
|
|
126
|
-
|
|
127
|
-
}, [selectedIds, agents])
|
|
130
|
+
}, [agents, bulkUpdateTasksMutation, clearSelection, selectedIds])
|
|
128
131
|
|
|
129
132
|
const handleBulkProject = useCallback(async (projectId: string | null) => {
|
|
130
133
|
if (selectedIds.size === 0) return
|
|
131
134
|
setBulkActing(true)
|
|
132
135
|
try {
|
|
133
|
-
await
|
|
134
|
-
await loadTasks()
|
|
136
|
+
await bulkUpdateTasksMutation.mutateAsync({ ids: [...selectedIds], patch: { projectId } })
|
|
135
137
|
toast.success(projectId ? `Assigned ${selectedIds.size} task(s) to project` : `Cleared project from ${selectedIds.size} task(s)`)
|
|
136
138
|
clearSelection()
|
|
137
139
|
} catch {
|
|
@@ -139,8 +141,7 @@ export default function TasksPage() {
|
|
|
139
141
|
} finally {
|
|
140
142
|
setBulkActing(false)
|
|
141
143
|
}
|
|
142
|
-
|
|
143
|
-
}, [selectedIds])
|
|
144
|
+
}, [bulkUpdateTasksMutation, clearSelection, selectedIds])
|
|
144
145
|
|
|
145
146
|
// Bulk action bar dropdowns
|
|
146
147
|
const [bulkAgentOpen, setBulkAgentOpen] = useState(false)
|
|
@@ -196,11 +197,7 @@ export default function TasksPage() {
|
|
|
196
197
|
window.history.replaceState(null, '', newUrl)
|
|
197
198
|
}, [activeProjectFilter, filterAgentId, filterTag, filtersHydrated, taskScopeFilter])
|
|
198
199
|
|
|
199
|
-
const
|
|
200
|
-
useEffect(() => {
|
|
201
|
-
Promise.all([loadTasks(), loadAgents(), loadProjects()]).then(() => setLoaded(true))
|
|
202
|
-
}, [loadAgents, loadProjects, loadTasks])
|
|
203
|
-
useWs('tasks', loadTasks, 5000)
|
|
200
|
+
const loaded = !tasksLoading && !agentsLoading && !projectsLoading
|
|
204
201
|
|
|
205
202
|
// Collect all unique tags across tasks
|
|
206
203
|
const allTags = dedup(Object.values(tasks).flatMap((t) => t.tags || [])).sort()
|
|
@@ -259,10 +256,8 @@ export default function TasksPage() {
|
|
|
259
256
|
const handleDrop = useCallback(async (taskId: string, newStatus: BoardTaskStatus) => {
|
|
260
257
|
const task = tasks[taskId]
|
|
261
258
|
if (!task || task.status === newStatus) return
|
|
262
|
-
await
|
|
263
|
-
|
|
264
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
265
|
-
}, [tasks])
|
|
259
|
+
await updateTaskMutation.mutateAsync({ id: taskId, patch: { status: newStatus } })
|
|
260
|
+
}, [tasks, updateTaskMutation])
|
|
266
261
|
|
|
267
262
|
const archivedCount = Object.values(tasks).filter((t) => t.status === 'archived').length
|
|
268
263
|
|
|
@@ -284,7 +279,7 @@ export default function TasksPage() {
|
|
|
284
279
|
try {
|
|
285
280
|
const rawLimit = Number.parseInt(githubLimit, 10)
|
|
286
281
|
const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 200)) : 25
|
|
287
|
-
const result = await
|
|
282
|
+
const result = await importGitHubIssuesMutation.mutateAsync({
|
|
288
283
|
repo: githubRepo.trim(),
|
|
289
284
|
token: githubToken.trim() || undefined,
|
|
290
285
|
state: githubState,
|
|
@@ -296,7 +291,6 @@ export default function TasksPage() {
|
|
|
296
291
|
projectId: activeProjectFilter,
|
|
297
292
|
})
|
|
298
293
|
setGitHubImportResult(result)
|
|
299
|
-
await loadTasks()
|
|
300
294
|
const summary = result.created.length > 0
|
|
301
295
|
? `Imported ${result.created.length} issue(s) from ${result.repo}`
|
|
302
296
|
: `No new issues imported from ${result.repo}`
|
|
@@ -316,7 +310,7 @@ export default function TasksPage() {
|
|
|
316
310
|
githubRepo,
|
|
317
311
|
githubState,
|
|
318
312
|
githubToken,
|
|
319
|
-
|
|
313
|
+
importGitHubIssuesMutation,
|
|
320
314
|
])
|
|
321
315
|
|
|
322
316
|
// Task counts per project (non-archived)
|
|
@@ -759,6 +753,9 @@ export default function TasksPage() {
|
|
|
759
753
|
<TaskColumn
|
|
760
754
|
status={status}
|
|
761
755
|
tasks={tasksByStatus(status)}
|
|
756
|
+
agents={agents}
|
|
757
|
+
projects={projects}
|
|
758
|
+
tasksById={tasks}
|
|
762
759
|
onDrop={handleDrop}
|
|
763
760
|
selectionMode={selectionMode}
|
|
764
761
|
selectedIds={selectedIds}
|
|
@@ -804,6 +801,9 @@ export default function TasksPage() {
|
|
|
804
801
|
<TaskCard
|
|
805
802
|
key={task.id}
|
|
806
803
|
task={task}
|
|
804
|
+
agents={agents}
|
|
805
|
+
projects={projects}
|
|
806
|
+
tasksById={tasks}
|
|
807
807
|
index={idx}
|
|
808
808
|
selectionMode={selectionMode}
|
|
809
809
|
selected={selectedIds.has(task.id)}
|
package/src/cli/index.js
CHANGED
|
@@ -596,6 +596,7 @@ const COMMAND_GROUPS = [
|
|
|
596
596
|
defaultBody: { action: 'status' },
|
|
597
597
|
}),
|
|
598
598
|
cmd('checkpoints', 'GET', '/chats/:id/checkpoints', 'List checkpoint history for a chat'),
|
|
599
|
+
cmd('migrate-messages', 'POST', '/chats/migrate-messages', 'Migrate messages from session blobs to relational table'),
|
|
599
600
|
],
|
|
600
601
|
},
|
|
601
602
|
{
|
package/src/cli/spec.js
CHANGED
|
@@ -428,6 +428,7 @@ const COMMAND_GROUPS = {
|
|
|
428
428
|
delete: { description: 'Delete one chat', method: 'DELETE', path: '/chats/:id', params: ['id'] },
|
|
429
429
|
'delete-many': { description: 'Delete multiple chats (body: {"ids":[...]})', method: 'DELETE', path: '/chats' },
|
|
430
430
|
'heartbeat-disable-all': { description: 'Disable all chat heartbeats and cancel queued heartbeat runs', method: 'POST', path: '/chats/heartbeat' },
|
|
431
|
+
'migrate-messages': { description: 'Migrate messages from session blobs to relational table', method: 'POST', path: '/chats/migrate-messages' },
|
|
431
432
|
messages: { description: 'Get chat message history', method: 'GET', path: '/chats/:id/messages', params: ['id'] },
|
|
432
433
|
'messages-update': { description: 'Update chat message metadata (e.g. bookmark)', method: 'PUT', path: '/chats/:id/messages', params: ['id'] },
|
|
433
434
|
'messages-send': { description: 'Append a user/system message to a chat', method: 'POST', path: '/chats/:id/messages', params: ['id'] },
|
|
@@ -447,7 +447,7 @@ export function AgentSheet() {
|
|
|
447
447
|
}))
|
|
448
448
|
setOpenclawEnabled(editing.provider === 'openclaw')
|
|
449
449
|
setProjectId(editing.projectId)
|
|
450
|
-
setAvatarSeed(editing.avatarSeed ||
|
|
450
|
+
setAvatarSeed(editing.avatarSeed || Math.random().toString(36).slice(2, 10))
|
|
451
451
|
setAvatarUrl(editing.avatarUrl || null)
|
|
452
452
|
setThinkingLevel(editing.thinkingLevel || '')
|
|
453
453
|
setMemoryScopeMode(editing.memoryScopeMode || 'auto')
|
|
@@ -527,7 +527,7 @@ export function AgentSheet() {
|
|
|
527
527
|
}))
|
|
528
528
|
setOpenclawEnabled(src.provider === 'openclaw')
|
|
529
529
|
setProjectId(src.projectId)
|
|
530
|
-
setAvatarSeed(
|
|
530
|
+
setAvatarSeed(Math.random().toString(36).slice(2, 10))
|
|
531
531
|
setAvatarUrl(null)
|
|
532
532
|
setThinkingLevel(src.thinkingLevel || '')
|
|
533
533
|
setMemoryScopeMode(src.memoryScopeMode || 'auto')
|
|
@@ -725,7 +725,7 @@ export function AgentSheet() {
|
|
|
725
725
|
|
|
726
726
|
const addRoutingTargetFromCurrent = () => {
|
|
727
727
|
const nextTarget: AgentRoutingTarget = {
|
|
728
|
-
id:
|
|
728
|
+
id: Math.random().toString(16).slice(2, 10),
|
|
729
729
|
label: routingTargets.length === 0 ? 'Primary route' : `Route ${routingTargets.length + 1}`,
|
|
730
730
|
role: routingTargets.length === 0 ? 'primary' : 'backup',
|
|
731
731
|
provider,
|
|
@@ -1165,7 +1165,7 @@ export function AgentSheet() {
|
|
|
1165
1165
|
type="button"
|
|
1166
1166
|
onClick={() => {
|
|
1167
1167
|
setAvatarUrl(null)
|
|
1168
|
-
if (!avatarSeed) setAvatarSeed(
|
|
1168
|
+
if (!avatarSeed) setAvatarSeed(Math.random().toString(36).slice(2, 10))
|
|
1169
1169
|
}}
|
|
1170
1170
|
className="text-[11px] text-text-3 hover:text-red-400 transition-colors self-start cursor-pointer"
|
|
1171
1171
|
>
|
|
@@ -1186,7 +1186,7 @@ export function AgentSheet() {
|
|
|
1186
1186
|
/>
|
|
1187
1187
|
<button
|
|
1188
1188
|
type="button"
|
|
1189
|
-
onClick={() => { setAvatarSeed(
|
|
1189
|
+
onClick={() => { setAvatarSeed(Math.random().toString(36).slice(2, 10)); setAvatarUrl(null) }}
|
|
1190
1190
|
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-[10px] border border-white/[0.08] bg-transparent text-text-3 text-[12px] font-600 cursor-pointer transition-all hover:bg-white/[0.04] hover:text-text-2 active:scale-95 shrink-0"
|
|
1191
1191
|
style={{ fontFamily: 'inherit' }}
|
|
1192
1192
|
title="Shuffle avatar"
|
|
@@ -119,7 +119,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
119
119
|
if (!editingProviderId && draftAgents.length === 0) {
|
|
120
120
|
const cp = configured
|
|
121
121
|
setDraftAgents([{
|
|
122
|
-
id: `auto:${
|
|
122
|
+
id: `auto:${Math.random().toString(36).slice(2, 10)}`,
|
|
123
123
|
templateId: 'auto',
|
|
124
124
|
name: 'Assistant',
|
|
125
125
|
description: 'A helpful assistant.',
|
|
@@ -140,7 +140,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
140
140
|
autoDraftSkillSuggestions: true,
|
|
141
141
|
orchestratorEnabled: false,
|
|
142
142
|
orchestratorMission: '',
|
|
143
|
-
avatarSeed:
|
|
143
|
+
avatarSeed: Math.random().toString(36).slice(2, 10),
|
|
144
144
|
avatarUrl: null,
|
|
145
145
|
enabled: true,
|
|
146
146
|
}])
|
|
@@ -182,7 +182,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
182
182
|
const addBlankAgent = () => {
|
|
183
183
|
const defaultProvider = configuredProviders[0] || null
|
|
184
184
|
const newAgent: StarterDraftAgent = {
|
|
185
|
-
id: `custom:${
|
|
185
|
+
id: `custom:${Math.random().toString(36).slice(2, 10)}`,
|
|
186
186
|
templateId: 'custom',
|
|
187
187
|
name: `Agent ${draftAgents.length + 1}`,
|
|
188
188
|
description: '',
|
|
@@ -203,7 +203,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
203
203
|
autoDraftSkillSuggestions: true,
|
|
204
204
|
orchestratorEnabled: false,
|
|
205
205
|
orchestratorMission: '',
|
|
206
|
-
avatarSeed:
|
|
206
|
+
avatarSeed: Math.random().toString(36).slice(2, 10),
|
|
207
207
|
avatarUrl: null,
|
|
208
208
|
enabled: true,
|
|
209
209
|
}
|
|
@@ -361,7 +361,7 @@ export function StepAgents({
|
|
|
361
361
|
/>
|
|
362
362
|
<button
|
|
363
363
|
type="button"
|
|
364
|
-
onClick={() => onUpdateDraft(draft.id, { avatarSeed:
|
|
364
|
+
onClick={() => onUpdateDraft(draft.id, { avatarSeed: Math.random().toString(36).slice(2, 10), avatarUrl: null })}
|
|
365
365
|
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-[10px] border border-white/[0.08] bg-transparent text-text-3 text-[12px] font-600 cursor-pointer transition-all hover:bg-white/[0.04] hover:text-text-2 active:scale-95 shrink-0"
|
|
366
366
|
title="Shuffle avatar"
|
|
367
367
|
>
|
|
@@ -216,7 +216,7 @@ export function StepConnect({
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
const configured: ConfiguredProvider = {
|
|
219
|
-
id:
|
|
219
|
+
id: Math.random().toString(16).slice(2, 10),
|
|
220
220
|
setupProvider: provider,
|
|
221
221
|
provider: resolvedProvider,
|
|
222
222
|
name: providerLabel.trim() || selectedProvider.name,
|
|
@@ -170,7 +170,7 @@ export function buildStarterDrafts(args: {
|
|
|
170
170
|
autoDraftSkillSuggestions: previous?.autoDraftSkillSuggestions ?? true,
|
|
171
171
|
orchestratorEnabled: previous?.orchestratorEnabled ?? false,
|
|
172
172
|
orchestratorMission: previous?.orchestratorMission || '',
|
|
173
|
-
avatarSeed: previous?.avatarSeed ||
|
|
173
|
+
avatarSeed: previous?.avatarSeed || Math.random().toString(36).slice(2, 10),
|
|
174
174
|
avatarUrl: previous?.avatarUrl || null,
|
|
175
175
|
enabled: previous?.enabled ?? true,
|
|
176
176
|
}
|
|
@@ -7,146 +7,10 @@ import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
|
7
7
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
8
8
|
import { toast } from 'sonner'
|
|
9
9
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
10
|
-
import type { Agent
|
|
10
|
+
import type { Agent } from '@/types'
|
|
11
11
|
import { CheckIcon } from '@/components/shared/check-icon'
|
|
12
12
|
import { WORKER_ONLY_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
13
13
|
|
|
14
|
-
function genRuleId(): string {
|
|
15
|
-
return `rule-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface RuleFormState {
|
|
19
|
-
type: 'keyword' | 'capability'
|
|
20
|
-
pattern: string
|
|
21
|
-
keywords: string
|
|
22
|
-
agentId: string
|
|
23
|
-
priority: number
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const emptyRuleForm: RuleFormState = {
|
|
27
|
-
type: 'keyword',
|
|
28
|
-
pattern: '',
|
|
29
|
-
keywords: '',
|
|
30
|
-
agentId: '',
|
|
31
|
-
priority: 10,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function RoutingRuleForm({
|
|
35
|
-
rule,
|
|
36
|
-
memberAgents,
|
|
37
|
-
onSave,
|
|
38
|
-
onCancel,
|
|
39
|
-
}: {
|
|
40
|
-
rule: RuleFormState
|
|
41
|
-
memberAgents: Agent[]
|
|
42
|
-
onSave: (form: RuleFormState) => void
|
|
43
|
-
onCancel: () => void
|
|
44
|
-
}) {
|
|
45
|
-
const [form, setForm] = useState<RuleFormState>(rule)
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div className="p-3 rounded-[8px] bg-white/[0.04] border border-white/[0.08] space-y-3">
|
|
49
|
-
<div className="flex gap-2">
|
|
50
|
-
{(['keyword', 'capability'] as const).map((t) => (
|
|
51
|
-
<button
|
|
52
|
-
key={t}
|
|
53
|
-
type="button"
|
|
54
|
-
onClick={() => setForm((f) => ({ ...f, type: t }))}
|
|
55
|
-
className={`flex-1 py-1.5 text-[11px] font-600 capitalize rounded-[6px] cursor-pointer transition-all ${
|
|
56
|
-
form.type === t
|
|
57
|
-
? 'bg-accent-soft text-accent-bright'
|
|
58
|
-
: 'bg-white/[0.04] text-text-3 hover:text-text-2'
|
|
59
|
-
}`}
|
|
60
|
-
>
|
|
61
|
-
{t}
|
|
62
|
-
</button>
|
|
63
|
-
))}
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
{form.type === 'keyword' && (
|
|
67
|
-
<>
|
|
68
|
-
<div>
|
|
69
|
-
<label className="block text-[11px] font-600 text-text-3 mb-1">Keywords (comma-separated)</label>
|
|
70
|
-
<input
|
|
71
|
-
type="text"
|
|
72
|
-
value={form.keywords}
|
|
73
|
-
onChange={(e) => setForm((f) => ({ ...f, keywords: e.target.value }))}
|
|
74
|
-
placeholder="e.g. deploy, devops, infrastructure"
|
|
75
|
-
className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
|
|
76
|
-
/>
|
|
77
|
-
</div>
|
|
78
|
-
<div>
|
|
79
|
-
<label className="block text-[11px] font-600 text-text-3 mb-1">Regex pattern (optional)</label>
|
|
80
|
-
<input
|
|
81
|
-
type="text"
|
|
82
|
-
value={form.pattern}
|
|
83
|
-
onChange={(e) => setForm((f) => ({ ...f, pattern: e.target.value }))}
|
|
84
|
-
placeholder="e.g. deploy|release|ship"
|
|
85
|
-
className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
|
|
86
|
-
/>
|
|
87
|
-
</div>
|
|
88
|
-
</>
|
|
89
|
-
)}
|
|
90
|
-
|
|
91
|
-
{form.type === 'capability' && (
|
|
92
|
-
<div>
|
|
93
|
-
<label className="block text-[11px] font-600 text-text-3 mb-1">Capability pattern</label>
|
|
94
|
-
<input
|
|
95
|
-
type="text"
|
|
96
|
-
value={form.pattern}
|
|
97
|
-
onChange={(e) => setForm((f) => ({ ...f, pattern: e.target.value }))}
|
|
98
|
-
placeholder="e.g. frontend, research, devops"
|
|
99
|
-
className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
|
|
100
|
-
/>
|
|
101
|
-
</div>
|
|
102
|
-
)}
|
|
103
|
-
|
|
104
|
-
<div className="flex gap-2">
|
|
105
|
-
<div className="flex-1">
|
|
106
|
-
<label className="block text-[11px] font-600 text-text-3 mb-1">Route to agent</label>
|
|
107
|
-
<select
|
|
108
|
-
value={form.agentId}
|
|
109
|
-
onChange={(e) => setForm((f) => ({ ...f, agentId: e.target.value }))}
|
|
110
|
-
className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text focus:outline-none focus:border-accent-bright/40"
|
|
111
|
-
>
|
|
112
|
-
<option value="">Select agent...</option>
|
|
113
|
-
{memberAgents.map((a) => (
|
|
114
|
-
<option key={a.id} value={a.id}>{a.name}</option>
|
|
115
|
-
))}
|
|
116
|
-
</select>
|
|
117
|
-
</div>
|
|
118
|
-
<div className="w-20">
|
|
119
|
-
<label className="block text-[11px] font-600 text-text-3 mb-1">Priority</label>
|
|
120
|
-
<input
|
|
121
|
-
type="number"
|
|
122
|
-
value={form.priority}
|
|
123
|
-
onChange={(e) => setForm((f) => ({ ...f, priority: parseInt(e.target.value, 10) || 0 }))}
|
|
124
|
-
className="w-full px-2.5 py-1.5 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text focus:outline-none focus:border-accent-bright/40"
|
|
125
|
-
/>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
|
|
129
|
-
<div className="flex gap-2 justify-end">
|
|
130
|
-
<button
|
|
131
|
-
type="button"
|
|
132
|
-
onClick={onCancel}
|
|
133
|
-
className="px-3 py-1.5 text-[11px] font-600 text-text-3 hover:text-text-2 cursor-pointer"
|
|
134
|
-
>
|
|
135
|
-
Cancel
|
|
136
|
-
</button>
|
|
137
|
-
<button
|
|
138
|
-
type="button"
|
|
139
|
-
onClick={() => onSave(form)}
|
|
140
|
-
disabled={!form.agentId || (form.type === 'keyword' && !form.keywords.trim() && !form.pattern.trim()) || (form.type === 'capability' && !form.pattern.trim())}
|
|
141
|
-
className="px-3 py-1.5 text-[11px] font-600 bg-accent-bright text-white rounded-[6px] hover:bg-accent-bright/90 disabled:opacity-50 cursor-pointer"
|
|
142
|
-
>
|
|
143
|
-
Save Rule
|
|
144
|
-
</button>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
14
|
export function ChatroomSheet() {
|
|
151
15
|
const open = useChatroomStore((s) => s.chatroomSheetOpen)
|
|
152
16
|
const editingId = useChatroomStore((s) => s.editingChatroomId)
|
|
@@ -163,10 +27,8 @@ export function ChatroomSheet() {
|
|
|
163
27
|
const [selectedAgentIds, setSelectedAgentIds] = useState<string[]>([])
|
|
164
28
|
const [chatMode, setChatMode] = useState<'sequential' | 'parallel'>('sequential')
|
|
165
29
|
const [autoAddress, setAutoAddress] = useState(false)
|
|
166
|
-
const [
|
|
30
|
+
const [routingGuidance, setRoutingGuidance] = useState('')
|
|
167
31
|
const [saving, setSaving] = useState(false)
|
|
168
|
-
const [addingRule, setAddingRule] = useState(false)
|
|
169
|
-
const [editingRuleId, setEditingRuleId] = useState<string | null>(null)
|
|
170
32
|
const [confirmDelete, setConfirmDelete] = useState(false)
|
|
171
33
|
|
|
172
34
|
const editing = editingId ? chatrooms[editingId] : null
|
|
@@ -178,17 +40,15 @@ export function ChatroomSheet() {
|
|
|
178
40
|
setSelectedAgentIds([...editing.agentIds])
|
|
179
41
|
setChatMode(editing.chatMode || 'sequential')
|
|
180
42
|
setAutoAddress(editing.autoAddress || false)
|
|
181
|
-
|
|
43
|
+
setRoutingGuidance(editing.routingGuidance || '')
|
|
182
44
|
} else {
|
|
183
45
|
setName('')
|
|
184
46
|
setDescription('')
|
|
185
47
|
setSelectedAgentIds([])
|
|
186
48
|
setChatMode('sequential')
|
|
187
49
|
setAutoAddress(false)
|
|
188
|
-
|
|
50
|
+
setRoutingGuidance('')
|
|
189
51
|
}
|
|
190
|
-
setAddingRule(false)
|
|
191
|
-
setEditingRuleId(null)
|
|
192
52
|
setConfirmDelete(false)
|
|
193
53
|
}, [editing, open])
|
|
194
54
|
|
|
@@ -206,7 +66,7 @@ export function ChatroomSheet() {
|
|
|
206
66
|
agentIds: selectedAgentIds,
|
|
207
67
|
chatMode,
|
|
208
68
|
autoAddress,
|
|
209
|
-
|
|
69
|
+
routingGuidance: routingGuidance.trim() || null,
|
|
210
70
|
}
|
|
211
71
|
if (editing) {
|
|
212
72
|
await updateChatroom(editing.id, payload)
|
|
@@ -245,53 +105,10 @@ export function ChatroomSheet() {
|
|
|
245
105
|
)
|
|
246
106
|
}
|
|
247
107
|
|
|
248
|
-
const handleAddRule = (form: RuleFormState) => {
|
|
249
|
-
const rule: ChatroomRoutingRule = {
|
|
250
|
-
id: genRuleId(),
|
|
251
|
-
type: form.type,
|
|
252
|
-
agentId: form.agentId,
|
|
253
|
-
priority: form.priority,
|
|
254
|
-
...(form.pattern.trim() ? { pattern: form.pattern.trim() } : {}),
|
|
255
|
-
...(form.type === 'keyword' && form.keywords.trim()
|
|
256
|
-
? { keywords: form.keywords.split(',').map((k) => k.trim()).filter(Boolean) }
|
|
257
|
-
: {}),
|
|
258
|
-
}
|
|
259
|
-
setRoutingRules((prev) => [...prev, rule].sort((a, b) => a.priority - b.priority))
|
|
260
|
-
setAddingRule(false)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const handleEditRule = (form: RuleFormState) => {
|
|
264
|
-
setRoutingRules((prev) =>
|
|
265
|
-
prev.map((r) =>
|
|
266
|
-
r.id === editingRuleId
|
|
267
|
-
? {
|
|
268
|
-
...r,
|
|
269
|
-
type: form.type,
|
|
270
|
-
agentId: form.agentId,
|
|
271
|
-
priority: form.priority,
|
|
272
|
-
pattern: form.pattern.trim() || undefined,
|
|
273
|
-
keywords:
|
|
274
|
-
form.type === 'keyword' && form.keywords.trim()
|
|
275
|
-
? form.keywords.split(',').map((k) => k.trim()).filter(Boolean)
|
|
276
|
-
: undefined,
|
|
277
|
-
}
|
|
278
|
-
: r,
|
|
279
|
-
).sort((a, b) => a.priority - b.priority),
|
|
280
|
-
)
|
|
281
|
-
setEditingRuleId(null)
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const removeRule = (ruleId: string) => {
|
|
285
|
-
setRoutingRules((prev) => prev.filter((r) => r.id !== ruleId))
|
|
286
|
-
}
|
|
287
|
-
|
|
288
108
|
const agentList = Object.values(agents).filter(
|
|
289
109
|
(a: Agent) => !a.trashedAt && !WORKER_ONLY_PROVIDER_IDS.has(a.provider)
|
|
290
110
|
) as Agent[]
|
|
291
111
|
|
|
292
|
-
const memberAgents = agentList.filter((a) => selectedAgentIds.includes(a.id))
|
|
293
|
-
const sortedRules = [...routingRules].sort((a, b) => a.priority - b.priority)
|
|
294
|
-
|
|
295
112
|
return (
|
|
296
113
|
<BottomSheet open={open} onClose={() => setChatroomSheetOpen(false)}>
|
|
297
114
|
<div className="p-6 max-w-[560px] mx-auto">
|
|
@@ -350,7 +167,7 @@ export function ChatroomSheet() {
|
|
|
350
167
|
<div>
|
|
351
168
|
<button
|
|
352
169
|
type="button"
|
|
353
|
-
onClick={() => setAutoAddress((
|
|
170
|
+
onClick={() => setAutoAddress((value) => !value)}
|
|
354
171
|
className="w-full flex items-center gap-2.5 px-3 py-2.5 rounded-[8px] border border-white/[0.08] bg-white/[0.03] cursor-pointer transition-all hover:bg-white/[0.05]"
|
|
355
172
|
>
|
|
356
173
|
<div className={`w-8 h-[18px] rounded-full transition-all relative ${autoAddress ? 'bg-accent-bright' : 'bg-white/[0.12]'}`}>
|
|
@@ -361,7 +178,7 @@ export function ChatroomSheet() {
|
|
|
361
178
|
<p className="text-[11px] text-text-3 mt-0.5">
|
|
362
179
|
{autoAddress
|
|
363
180
|
? 'Every message is sent to all agents, no @mention needed'
|
|
364
|
-
: 'Only agents you @mention
|
|
181
|
+
: 'Only agents you @mention respond unless routing guidance selects someone'}
|
|
365
182
|
</p>
|
|
366
183
|
</div>
|
|
367
184
|
</button>
|
|
@@ -405,95 +222,18 @@ export function ChatroomSheet() {
|
|
|
405
222
|
)}
|
|
406
223
|
</div>
|
|
407
224
|
|
|
408
|
-
{/* Routing Rules */}
|
|
409
225
|
<div>
|
|
410
|
-
<label className="block text-[12px] font-600 text-text-2 mb-1.5">
|
|
411
|
-
Routing Rules ({sortedRules.length})
|
|
412
|
-
</label>
|
|
226
|
+
<label className="block text-[12px] font-600 text-text-2 mb-1.5">Routing Guidance</label>
|
|
413
227
|
<p className="text-[11px] text-text-3 mb-2">
|
|
414
|
-
|
|
228
|
+
Optional. Used only when there is no explicit @mention and auto-address is off. Describe which kinds of messages should go to which members.
|
|
415
229
|
</p>
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
<RoutingRuleForm
|
|
424
|
-
key={rule.id}
|
|
425
|
-
rule={{
|
|
426
|
-
type: rule.type,
|
|
427
|
-
pattern: rule.pattern || '',
|
|
428
|
-
keywords: rule.keywords?.join(', ') || '',
|
|
429
|
-
agentId: rule.agentId,
|
|
430
|
-
priority: rule.priority,
|
|
431
|
-
}}
|
|
432
|
-
memberAgents={memberAgents}
|
|
433
|
-
onSave={handleEditRule}
|
|
434
|
-
onCancel={() => setEditingRuleId(null)}
|
|
435
|
-
/>
|
|
436
|
-
)
|
|
437
|
-
}
|
|
438
|
-
return (
|
|
439
|
-
<div
|
|
440
|
-
key={rule.id}
|
|
441
|
-
className="flex items-center gap-2 px-3 py-2 rounded-[8px] bg-white/[0.04] border border-white/[0.08]"
|
|
442
|
-
>
|
|
443
|
-
<span className="text-[10px] font-700 text-text-3 bg-white/[0.06] px-1.5 py-0.5 rounded">
|
|
444
|
-
P{rule.priority}
|
|
445
|
-
</span>
|
|
446
|
-
<span className="text-[10px] font-600 text-accent-bright/70 uppercase">
|
|
447
|
-
{rule.type}
|
|
448
|
-
</span>
|
|
449
|
-
<span className="text-[12px] text-text-2 flex-1 truncate">
|
|
450
|
-
{rule.type === 'keyword'
|
|
451
|
-
? (rule.keywords?.join(', ') || rule.pattern || '(no match)')
|
|
452
|
-
: (rule.pattern || '(no pattern)')}
|
|
453
|
-
</span>
|
|
454
|
-
<span className="text-[11px] text-text-3 truncate max-w-[100px]">
|
|
455
|
-
{agent?.name || 'Unknown'}
|
|
456
|
-
</span>
|
|
457
|
-
<button
|
|
458
|
-
type="button"
|
|
459
|
-
onClick={() => setEditingRuleId(rule.id)}
|
|
460
|
-
className="text-[11px] text-text-3 hover:text-text-2 cursor-pointer px-1"
|
|
461
|
-
>
|
|
462
|
-
Edit
|
|
463
|
-
</button>
|
|
464
|
-
<button
|
|
465
|
-
type="button"
|
|
466
|
-
onClick={() => removeRule(rule.id)}
|
|
467
|
-
className="text-[11px] text-red-400 hover:text-red-300 cursor-pointer px-1"
|
|
468
|
-
>
|
|
469
|
-
Remove
|
|
470
|
-
</button>
|
|
471
|
-
</div>
|
|
472
|
-
)
|
|
473
|
-
})}
|
|
474
|
-
</div>
|
|
475
|
-
)}
|
|
476
|
-
|
|
477
|
-
{addingRule ? (
|
|
478
|
-
<RoutingRuleForm
|
|
479
|
-
rule={emptyRuleForm}
|
|
480
|
-
memberAgents={memberAgents}
|
|
481
|
-
onSave={handleAddRule}
|
|
482
|
-
onCancel={() => setAddingRule(false)}
|
|
483
|
-
/>
|
|
484
|
-
) : (
|
|
485
|
-
<button
|
|
486
|
-
type="button"
|
|
487
|
-
onClick={() => setAddingRule(true)}
|
|
488
|
-
disabled={memberAgents.length === 0}
|
|
489
|
-
className="w-full py-2 rounded-[8px] border border-dashed border-white/[0.12] text-[12px] font-600 text-text-3 hover:text-text-2 hover:border-white/[0.2] cursor-pointer transition-all disabled:opacity-40 disabled:cursor-not-allowed"
|
|
490
|
-
>
|
|
491
|
-
+ Add Rule
|
|
492
|
-
</button>
|
|
493
|
-
)}
|
|
494
|
-
{memberAgents.length === 0 && (
|
|
495
|
-
<p className="text-[11px] text-text-3 mt-1">Add members first to create routing rules.</p>
|
|
496
|
-
)}
|
|
230
|
+
<textarea
|
|
231
|
+
value={routingGuidance}
|
|
232
|
+
onChange={(e) => setRoutingGuidance(e.target.value)}
|
|
233
|
+
placeholder={'Examples:\nRoute deployment issues to Ops.\nPrefer Maya for design reviews and UI polish.\nSend pricing or market-analysis requests to Research.'}
|
|
234
|
+
rows={6}
|
|
235
|
+
className="w-full px-3 py-2 rounded-[8px] bg-white/[0.06] border border-white/[0.08] text-[13px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40 resize-y min-h-[132px]"
|
|
236
|
+
/>
|
|
497
237
|
</div>
|
|
498
238
|
</div>
|
|
499
239
|
|