@swarmclawai/swarmclaw 1.3.4 → 1.3.5
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 +15 -76
- package/package.json +1 -1
- package/skills/swarmclaw.md +17 -0
- package/src/app/api/agents/[id]/dream/route.ts +45 -0
- package/src/app/api/knowledge/[id]/route.ts +48 -49
- package/src/app/api/knowledge/hygiene/route.ts +13 -0
- package/src/app/api/knowledge/route.ts +70 -42
- package/src/app/api/knowledge/sources/[id]/archive/route.ts +15 -0
- package/src/app/api/knowledge/sources/[id]/restore/route.ts +10 -0
- package/src/app/api/knowledge/sources/[id]/route.ts +1 -0
- package/src/app/api/knowledge/sources/[id]/supersede/route.ts +26 -0
- package/src/app/api/knowledge/sources/[id]/sync/route.ts +17 -0
- package/src/app/api/knowledge/sources/route.ts +1 -0
- package/src/app/api/knowledge/upload/route.ts +3 -51
- package/src/app/api/memory/dream/[id]/route.ts +19 -0
- package/src/app/api/memory/dream/route.ts +34 -0
- package/src/app/knowledge/layout.tsx +1 -1
- package/src/app/knowledge/page.tsx +2 -22
- package/src/app/protocols/page.tsx +21 -2
- package/src/cli/index.js +16 -0
- package/src/cli/spec.js +5 -0
- package/src/components/agents/agent-sheet.tsx +65 -0
- package/src/components/chat/message-bubble.tsx +10 -0
- package/src/components/knowledge/grounding-panel.tsx +99 -0
- package/src/components/knowledge/knowledge-detail.tsx +402 -0
- package/src/components/knowledge/knowledge-list.tsx +351 -126
- package/src/components/knowledge/knowledge-sheet.tsx +208 -119
- package/src/components/memory/dream-history.tsx +155 -0
- package/src/components/memory/memory-card.tsx +7 -0
- package/src/components/memory/memory-detail.tsx +46 -0
- package/src/components/runs/run-list.tsx +23 -0
- package/src/lib/server/api-routes.test.ts +43 -2
- package/src/lib/server/chat-execution/chat-execution-grounding.test.ts +127 -0
- package/src/lib/server/chat-execution/chat-execution-types.ts +8 -1
- package/src/lib/server/chat-execution/chat-execution.ts +1 -0
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +21 -1
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +6 -1
- package/src/lib/server/chat-execution/post-stream-finalization.ts +15 -3
- package/src/lib/server/chat-execution/prompt-sections.ts +29 -3
- package/src/lib/server/chat-execution/stream-agent-chat.ts +6 -1
- package/src/lib/server/execution-engine/task-attempt.ts +8 -2
- package/src/lib/server/knowledge-import.ts +159 -0
- package/src/lib/server/knowledge-sources.test.ts +215 -0
- package/src/lib/server/knowledge-sources.ts +1266 -0
- package/src/lib/server/memory/dream-cycles.ts +49 -0
- package/src/lib/server/memory/dream-idle-callback.ts +38 -0
- package/src/lib/server/memory/dream-service.ts +315 -0
- package/src/lib/server/memory/memory-db.ts +37 -2
- package/src/lib/server/protocols/protocol-agent-turn.ts +7 -0
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +19 -6
- package/src/lib/server/protocols/protocol-service.test.ts +99 -0
- package/src/lib/server/protocols/protocol-step-helpers.ts +7 -1
- package/src/lib/server/protocols/protocol-step-processors.ts +16 -3
- package/src/lib/server/protocols/protocol-types.ts +4 -0
- package/src/lib/server/runtime/daemon-state/core.ts +6 -1
- package/src/lib/server/runtime/run-ledger.test.ts +120 -0
- package/src/lib/server/runtime/run-ledger.ts +27 -1
- package/src/lib/server/runtime/session-run-manager/drain.ts +5 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +19 -2
- package/src/lib/server/storage-normalization.ts +5 -0
- package/src/lib/server/storage.ts +15 -0
- package/src/stores/slices/ui-slice.ts +4 -0
- package/src/types/agent.ts +7 -0
- package/src/types/dream.ts +45 -0
- package/src/types/index.ts +1 -0
- package/src/types/message.ts +3 -0
- package/src/types/misc.ts +131 -0
- package/src/types/protocol.ts +4 -0
- package/src/types/run.ts +4 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { log } from '@/lib/server/logger'
|
|
3
|
+
|
|
4
|
+
const TAG = 'api-dream'
|
|
5
|
+
|
|
6
|
+
export async function GET(req: Request) {
|
|
7
|
+
try {
|
|
8
|
+
const { searchParams } = new URL(req.url)
|
|
9
|
+
const agentId = searchParams.get('agentId') ?? undefined
|
|
10
|
+
const limit = Math.max(1, Math.min(500, Number(searchParams.get('limit')) || 50))
|
|
11
|
+
const { listDreamCycles } = await import('@/lib/server/memory/dream-cycles')
|
|
12
|
+
const cycles = listDreamCycles(agentId, limit)
|
|
13
|
+
return NextResponse.json({ ok: true, cycles })
|
|
14
|
+
} catch (err: unknown) {
|
|
15
|
+
log.error(TAG, 'GET failed:', err)
|
|
16
|
+
return NextResponse.json({ ok: false, error: String((err as Error)?.message || err) }, { status: 500 })
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function POST(req: Request) {
|
|
21
|
+
try {
|
|
22
|
+
const body = await req.json().catch(() => ({})) as Record<string, unknown>
|
|
23
|
+
const agentId = typeof body.agentId === 'string' ? body.agentId : undefined
|
|
24
|
+
if (!agentId) {
|
|
25
|
+
return NextResponse.json({ ok: false, error: 'agentId is required' }, { status: 400 })
|
|
26
|
+
}
|
|
27
|
+
const { executeDreamCycle } = await import('@/lib/server/memory/dream-service')
|
|
28
|
+
const cycle = await executeDreamCycle(agentId, 'manual')
|
|
29
|
+
return NextResponse.json({ ok: true, cycle })
|
|
30
|
+
} catch (err: unknown) {
|
|
31
|
+
log.error(TAG, 'POST failed:', err)
|
|
32
|
+
return NextResponse.json({ ok: false, error: String((err as Error)?.message || err) }, { status: 500 })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -10,7 +10,7 @@ export default function KnowledgeLayout({ children }: { children: React.ReactNod
|
|
|
10
10
|
<>
|
|
11
11
|
<SidebarPanelShell
|
|
12
12
|
title="Knowledge"
|
|
13
|
-
createLabel="
|
|
13
|
+
createLabel="New Source"
|
|
14
14
|
onNew={() => useAppStore.getState().setKnowledgeSheetOpen(true)}
|
|
15
15
|
>
|
|
16
16
|
<KnowledgeList />
|
|
@@ -1,27 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { KnowledgeList } from '@/components/knowledge/knowledge-list'
|
|
3
|
+
import { KnowledgeDetail } from '@/components/knowledge/knowledge-detail'
|
|
5
4
|
|
|
6
5
|
export default function KnowledgePage() {
|
|
7
|
-
return
|
|
8
|
-
<div className="flex-1 flex flex-col h-full">
|
|
9
|
-
<div className="flex items-center px-6 pt-5 pb-3 shrink-0">
|
|
10
|
-
<h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">
|
|
11
|
-
Knowledge
|
|
12
|
-
</h2>
|
|
13
|
-
<button
|
|
14
|
-
onClick={() => useAppStore.getState().setKnowledgeSheetOpen(true)}
|
|
15
|
-
className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-accent-bright/15 transition-all cursor-pointer"
|
|
16
|
-
style={{ fontFamily: 'inherit' }}
|
|
17
|
-
>
|
|
18
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
|
|
19
|
-
<line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
|
|
20
|
-
</svg>
|
|
21
|
-
Knowledge Entry
|
|
22
|
-
</button>
|
|
23
|
-
</div>
|
|
24
|
-
<KnowledgeList />
|
|
25
|
-
</div>
|
|
26
|
-
)
|
|
6
|
+
return <KnowledgeDetail />
|
|
27
7
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { useTasksQuery } from '@/features/tasks/queries'
|
|
17
17
|
import { MainContent } from '@/components/layout/main-content'
|
|
18
18
|
import { StructuredSessionLauncher } from '@/components/protocols/structured-session-launcher'
|
|
19
|
+
import { GroundingPanel } from '@/components/knowledge/grounding-panel'
|
|
19
20
|
import { timeAgo } from '@/lib/time-format'
|
|
20
21
|
import type {
|
|
21
22
|
BoardTask,
|
|
@@ -164,8 +165,8 @@ export default function ProtocolsPage() {
|
|
|
164
165
|
const runActionMutation = useProtocolRunActionMutation()
|
|
165
166
|
const upsertTemplateMutation = useUpsertProtocolTemplateMutation()
|
|
166
167
|
const deleteTemplateMutation = useDeleteProtocolTemplateMutation()
|
|
167
|
-
const templates = templatesQuery.data ?? []
|
|
168
|
-
const runs = runsQuery.data ?? []
|
|
168
|
+
const templates = useMemo(() => templatesQuery.data ?? [], [templatesQuery.data])
|
|
169
|
+
const runs = useMemo(() => runsQuery.data ?? [], [runsQuery.data])
|
|
169
170
|
const detail = detailQuery.data ?? null
|
|
170
171
|
const agents = agentsQuery.data ?? {}
|
|
171
172
|
const chatrooms = chatroomsQuery.data ?? {}
|
|
@@ -203,6 +204,14 @@ export default function ProtocolsPage() {
|
|
|
203
204
|
const selectedTemplate = useMemo(() => templates.find((template) => template.id === form.templateId) || null, [form.templateId, templates])
|
|
204
205
|
const customTemplates = useMemo(() => templates.filter((template) => !template.builtIn), [templates])
|
|
205
206
|
const builtInTemplates = useMemo(() => templates.filter((template) => template.builtIn), [templates])
|
|
207
|
+
const artifactCitationMap = useMemo(() => {
|
|
208
|
+
const map = new Map<string, ProtocolRunEvent['citations']>()
|
|
209
|
+
for (const event of detail?.events || []) {
|
|
210
|
+
if (!event.artifactId || !event.citations?.length) continue
|
|
211
|
+
map.set(event.artifactId, event.citations)
|
|
212
|
+
}
|
|
213
|
+
return map
|
|
214
|
+
}, [detail?.events])
|
|
206
215
|
const filteredRuns = useMemo(() => {
|
|
207
216
|
const search = runSearch.trim().toLowerCase()
|
|
208
217
|
return runs.filter((run) => {
|
|
@@ -1167,6 +1176,11 @@ export default function ProtocolsPage() {
|
|
|
1167
1176
|
<span className="text-[11px] text-text-3/60">{artifact.kind.replace(/_/g, ' ')}</span>
|
|
1168
1177
|
</div>
|
|
1169
1178
|
<div className="mt-2 whitespace-pre-wrap text-[13px] leading-relaxed text-text-2">{artifact.content}</div>
|
|
1179
|
+
{artifactCitationMap.get(artifact.id)?.length ? (
|
|
1180
|
+
<div className="mt-3">
|
|
1181
|
+
<GroundingPanel citations={artifactCitationMap.get(artifact.id)} compact />
|
|
1182
|
+
</div>
|
|
1183
|
+
) : null}
|
|
1170
1184
|
</div>
|
|
1171
1185
|
))}
|
|
1172
1186
|
</div>
|
|
@@ -1220,6 +1234,11 @@ export default function ProtocolsPage() {
|
|
|
1220
1234
|
<div className="text-[11px] text-text-3/55">{timeAgo(event.createdAt)}</div>
|
|
1221
1235
|
</div>
|
|
1222
1236
|
<div className="mt-2 text-[13px] leading-relaxed text-text-2">{event.summary}</div>
|
|
1237
|
+
{event.citations?.length ? (
|
|
1238
|
+
<div className="mt-3">
|
|
1239
|
+
<GroundingPanel citations={event.citations} compact />
|
|
1240
|
+
</div>
|
|
1241
|
+
) : null}
|
|
1223
1242
|
</div>
|
|
1224
1243
|
))}
|
|
1225
1244
|
</div>
|
package/src/cli/index.js
CHANGED
|
@@ -26,6 +26,8 @@ const COMMAND_GROUPS = [
|
|
|
26
26
|
cmd('clone', 'POST', '/agents/:id/clone', 'Clone an agent'),
|
|
27
27
|
cmd('bulk-update', 'PATCH', '/agents/bulk', 'Bulk update agents', { expectsJsonBody: true }),
|
|
28
28
|
cmd('status', 'GET', '/agents/:id/status', 'Get live status for an agent'),
|
|
29
|
+
cmd('dream', 'GET', '/agents/:id/dream', 'Get agent dream config and recent cycles'),
|
|
30
|
+
cmd('dream-update', 'PATCH', '/agents/:id/dream', 'Update agent dream config', { expectsJsonBody: true }),
|
|
29
31
|
],
|
|
30
32
|
},
|
|
31
33
|
{
|
|
@@ -268,6 +270,17 @@ const COMMAND_GROUPS = [
|
|
|
268
270
|
requestType: 'upload',
|
|
269
271
|
inputPositional: 'filePath',
|
|
270
272
|
}),
|
|
273
|
+
cmd('hygiene', 'GET', '/knowledge/hygiene', 'Get knowledge hygiene summary'),
|
|
274
|
+
cmd('hygiene-run', 'POST', '/knowledge/hygiene', 'Run knowledge hygiene maintenance'),
|
|
275
|
+
cmd('sources', 'GET', '/knowledge/sources', 'List knowledge sources'),
|
|
276
|
+
cmd('source-create', 'POST', '/knowledge/sources', 'Create knowledge source', { expectsJsonBody: true }),
|
|
277
|
+
cmd('source-get', 'GET', '/knowledge/sources/:id', 'Get knowledge source detail'),
|
|
278
|
+
cmd('source-update', 'PUT', '/knowledge/sources/:id', 'Update knowledge source', { expectsJsonBody: true }),
|
|
279
|
+
cmd('source-delete', 'DELETE', '/knowledge/sources/:id', 'Delete knowledge source'),
|
|
280
|
+
cmd('source-archive', 'POST', '/knowledge/sources/:id/archive', 'Archive knowledge source', { expectsJsonBody: true }),
|
|
281
|
+
cmd('source-restore', 'POST', '/knowledge/sources/:id/restore', 'Restore archived knowledge source'),
|
|
282
|
+
cmd('source-supersede', 'POST', '/knowledge/sources/:id/supersede', 'Mark source as superseded', { expectsJsonBody: true }),
|
|
283
|
+
cmd('source-sync', 'POST', '/knowledge/sources/:id/sync', 'Re-sync file/URL knowledge source'),
|
|
271
284
|
],
|
|
272
285
|
},
|
|
273
286
|
{
|
|
@@ -290,6 +303,9 @@ const COMMAND_GROUPS = [
|
|
|
290
303
|
cmd('maintenance', 'GET', '/memory/maintenance', 'Analyze memory dedupe/prune candidates'),
|
|
291
304
|
cmd('maintenance-run', 'POST', '/memory/maintenance', 'Run memory dedupe/prune maintenance', { expectsJsonBody: true }),
|
|
292
305
|
cmd('graph', 'GET', '/memory/graph', 'Get memory graph (nodes and links) for visualization'),
|
|
306
|
+
cmd('dream', 'GET', '/memory/dream', 'List dream cycles'),
|
|
307
|
+
cmd('dream-trigger', 'POST', '/memory/dream', 'Trigger a dream cycle', { expectsJsonBody: true }),
|
|
308
|
+
cmd('dream-get', 'GET', '/memory/dream/:id', 'Get dream cycle by id'),
|
|
293
309
|
],
|
|
294
310
|
},
|
|
295
311
|
{
|
package/src/cli/spec.js
CHANGED
|
@@ -12,6 +12,8 @@ const COMMAND_GROUPS = {
|
|
|
12
12
|
restore: { description: 'Restore a trashed agent', method: 'POST', path: '/agents/trash' },
|
|
13
13
|
purge: { description: 'Permanently delete a trashed agent', method: 'DELETE', path: '/agents/trash' },
|
|
14
14
|
status: { description: 'Get live status for an agent', method: 'GET', path: '/agents/:id/status', params: ['id'] },
|
|
15
|
+
dream: { description: 'Get agent dream config and recent cycles', method: 'GET', path: '/agents/:id/dream', params: ['id'] },
|
|
16
|
+
'dream-update': { description: 'Update agent dream config', method: 'PATCH', path: '/agents/:id/dream', params: ['id'] },
|
|
15
17
|
},
|
|
16
18
|
},
|
|
17
19
|
activity: {
|
|
@@ -218,6 +220,9 @@ const COMMAND_GROUPS = {
|
|
|
218
220
|
delete: { description: 'Delete memory entry', method: 'DELETE', path: '/memory/:id', params: ['id'] },
|
|
219
221
|
maintenance: { description: 'Analyze memory dedupe/prune candidates', method: 'GET', path: '/memory/maintenance' },
|
|
220
222
|
'maintenance-run': { description: 'Run memory dedupe/prune maintenance', method: 'POST', path: '/memory/maintenance' },
|
|
223
|
+
dream: { description: 'List dream cycles', method: 'GET', path: '/memory/dream' },
|
|
224
|
+
'dream-trigger': { description: 'Trigger a dream cycle', method: 'POST', path: '/memory/dream' },
|
|
225
|
+
'dream-get': { description: 'Get dream cycle by id', method: 'GET', path: '/memory/dream/:id', params: ['id'] },
|
|
221
226
|
},
|
|
222
227
|
},
|
|
223
228
|
'memory-images': {
|
|
@@ -236,6 +236,9 @@ export function AgentSheet() {
|
|
|
236
236
|
const [heartbeatIntervalSec, setHeartbeatIntervalSec] = useState('') // '' = default (30m)
|
|
237
237
|
const [heartbeatModel, setHeartbeatModel] = useState('')
|
|
238
238
|
const [heartbeatPrompt, setHeartbeatPrompt] = useState('')
|
|
239
|
+
const [dreamEnabled, setDreamEnabled] = useState(false)
|
|
240
|
+
const [dreamCooldownMinutes, setDreamCooldownMinutes] = useState('360')
|
|
241
|
+
const [dreamTier2Enabled, setDreamTier2Enabled] = useState(true)
|
|
239
242
|
const [orchestratorEnabled, setOrchestratorEnabled] = useState(false)
|
|
240
243
|
const [orchestratorMission, setOrchestratorMission] = useState('')
|
|
241
244
|
const [orchestratorWakeInterval, setOrchestratorWakeInterval] = useState('5m')
|
|
@@ -437,6 +440,9 @@ export function AgentSheet() {
|
|
|
437
440
|
setHeartbeatIntervalSec(parseDurationToSec(editing.heartbeatInterval, editing.heartbeatIntervalSec))
|
|
438
441
|
setHeartbeatModel(editing.heartbeatModel || '')
|
|
439
442
|
setHeartbeatPrompt(editing.heartbeatPrompt || '')
|
|
443
|
+
setDreamEnabled(editing.dreamEnabled || false)
|
|
444
|
+
setDreamCooldownMinutes(editing.dreamConfig?.cooldownMinutes != null ? String(editing.dreamConfig.cooldownMinutes) : '360')
|
|
445
|
+
setDreamTier2Enabled(editing.dreamConfig?.tier2Enabled !== false)
|
|
440
446
|
setOrchestratorEnabled(editing.orchestratorEnabled || false)
|
|
441
447
|
setOrchestratorMission(editing.orchestratorMission || '')
|
|
442
448
|
setOrchestratorWakeInterval(typeof editing.orchestratorWakeInterval === 'string' ? editing.orchestratorWakeInterval : typeof editing.orchestratorWakeInterval === 'number' ? `${editing.orchestratorWakeInterval}s` : '5m')
|
|
@@ -516,6 +522,9 @@ export function AgentSheet() {
|
|
|
516
522
|
setHeartbeatIntervalSec(parseDurationToSec(src.heartbeatInterval, src.heartbeatIntervalSec))
|
|
517
523
|
setHeartbeatModel(src.heartbeatModel || '')
|
|
518
524
|
setHeartbeatPrompt(src.heartbeatPrompt || '')
|
|
525
|
+
setDreamEnabled(src.dreamEnabled || false)
|
|
526
|
+
setDreamCooldownMinutes(src.dreamConfig?.cooldownMinutes != null ? String(src.dreamConfig.cooldownMinutes) : '360')
|
|
527
|
+
setDreamTier2Enabled(src.dreamConfig?.tier2Enabled !== false)
|
|
519
528
|
setOrchestratorEnabled(src.orchestratorEnabled || false)
|
|
520
529
|
setOrchestratorMission(src.orchestratorMission || '')
|
|
521
530
|
setOrchestratorWakeInterval(typeof src.orchestratorWakeInterval === 'string' ? src.orchestratorWakeInterval : typeof src.orchestratorWakeInterval === 'number' ? `${src.orchestratorWakeInterval}s` : '5m')
|
|
@@ -794,6 +803,10 @@ export function AgentSheet() {
|
|
|
794
803
|
heartbeatIntervalSec: heartbeatIntervalSec ? Number(heartbeatIntervalSec) : null,
|
|
795
804
|
heartbeatModel: heartbeatModel.trim() || null,
|
|
796
805
|
heartbeatPrompt: heartbeatPrompt.trim() || null,
|
|
806
|
+
dreamEnabled,
|
|
807
|
+
dreamConfig: dreamEnabled
|
|
808
|
+
? { cooldownMinutes: Number(dreamCooldownMinutes) || 360, tier2Enabled: dreamTier2Enabled }
|
|
809
|
+
: null,
|
|
797
810
|
orchestratorEnabled,
|
|
798
811
|
orchestratorMission: orchestratorMission.trim() || undefined,
|
|
799
812
|
orchestratorWakeInterval: orchestratorWakeInterval.trim() || null,
|
|
@@ -818,6 +831,8 @@ export function AgentSheet() {
|
|
|
818
831
|
data.heartbeatIntervalSec = null
|
|
819
832
|
data.heartbeatModel = null
|
|
820
833
|
data.heartbeatPrompt = null
|
|
834
|
+
data.dreamEnabled = false
|
|
835
|
+
data.dreamConfig = null
|
|
821
836
|
}
|
|
822
837
|
const savedAgent = editing
|
|
823
838
|
? await updateAgent(editing.id, data)
|
|
@@ -1007,6 +1022,7 @@ export function AgentSheet() {
|
|
|
1007
1022
|
if (skills.length > 0 || skillIds.length > 0 || mcpServerIds.length > 0 || mcpDisabledTools.length > 0) badges.push('Skills & MCP')
|
|
1008
1023
|
if (toolsDifferFromDefault || filesystemScope === 'machine') badges.push('Tools')
|
|
1009
1024
|
if (budgetEnabled) badges.push('Budget')
|
|
1025
|
+
if (dreamEnabled) badges.push('Dreaming')
|
|
1010
1026
|
if (disabled) badges.push('Disabled')
|
|
1011
1027
|
if (autoRecovery) badges.push('Recovery')
|
|
1012
1028
|
if (projectId) badges.push('Project')
|
|
@@ -1018,6 +1034,7 @@ export function AgentSheet() {
|
|
|
1018
1034
|
autoRecovery,
|
|
1019
1035
|
budgetEnabled,
|
|
1020
1036
|
disabled,
|
|
1037
|
+
dreamEnabled,
|
|
1021
1038
|
fallbackCredentialIds.length,
|
|
1022
1039
|
filesystemScope,
|
|
1023
1040
|
identityPersonaLabel,
|
|
@@ -1905,6 +1922,54 @@ export function AgentSheet() {
|
|
|
1905
1922
|
<span className={`absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white transition-transform duration-200 ${heartbeatEnabled ? 'translate-x-5' : 'translate-x-0'}`} />
|
|
1906
1923
|
</button>
|
|
1907
1924
|
</div>
|
|
1925
|
+
<div className="flex items-center justify-between gap-4 rounded-[14px] border border-white/[0.06] bg-white/[0.02] px-4 py-4 mt-3">
|
|
1926
|
+
<div className="min-w-0">
|
|
1927
|
+
<div className="flex items-center gap-2">
|
|
1928
|
+
<p className="text-[14px] font-600 text-text">Dreaming</p>
|
|
1929
|
+
<HintTip text="When enabled, this agent consolidates and optimizes its memories during idle periods" />
|
|
1930
|
+
</div>
|
|
1931
|
+
<p className="mt-1 text-[12px] leading-[1.6] text-text-3/75">
|
|
1932
|
+
Consolidate, decay, and reflect on memories when the agent is idle.
|
|
1933
|
+
</p>
|
|
1934
|
+
</div>
|
|
1935
|
+
<button
|
|
1936
|
+
type="button"
|
|
1937
|
+
onClick={() => setDreamEnabled((current) => !current)}
|
|
1938
|
+
className={`relative h-6 w-11 shrink-0 rounded-full border-none transition-colors duration-200 ${dreamEnabled ? 'bg-accent-bright' : 'bg-white/[0.12]'}`}
|
|
1939
|
+
aria-pressed={dreamEnabled}
|
|
1940
|
+
>
|
|
1941
|
+
<span className={`absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white transition-transform duration-200 ${dreamEnabled ? 'translate-x-5' : 'translate-x-0'}`} />
|
|
1942
|
+
</button>
|
|
1943
|
+
</div>
|
|
1944
|
+
{dreamEnabled && (
|
|
1945
|
+
<div className="mt-3 rounded-[14px] border border-white/[0.04] bg-white/[0.01] px-4 py-4 space-y-3">
|
|
1946
|
+
<div>
|
|
1947
|
+
<label className="flex items-center gap-2 text-[12px] font-600 text-text-2 mb-1.5">
|
|
1948
|
+
Cooldown (minutes) <HintTip text="Minimum minutes between dream cycles" />
|
|
1949
|
+
</label>
|
|
1950
|
+
<input
|
|
1951
|
+
type="number"
|
|
1952
|
+
value={dreamCooldownMinutes}
|
|
1953
|
+
onChange={(e) => setDreamCooldownMinutes(e.target.value)}
|
|
1954
|
+
min={1}
|
|
1955
|
+
placeholder="360"
|
|
1956
|
+
className={inputClass}
|
|
1957
|
+
style={{ fontFamily: 'inherit' }}
|
|
1958
|
+
/>
|
|
1959
|
+
</div>
|
|
1960
|
+
<label className="flex items-center gap-3 cursor-pointer">
|
|
1961
|
+
<div
|
|
1962
|
+
onClick={() => setDreamTier2Enabled((current) => !current)}
|
|
1963
|
+
className={`w-11 h-6 rounded-full transition-all duration-200 relative cursor-pointer shrink-0 ${dreamTier2Enabled ? 'bg-accent-bright' : 'bg-white/[0.08]'}`}
|
|
1964
|
+
>
|
|
1965
|
+
<div className={`absolute top-0.5 w-5 h-5 rounded-full bg-white transition-all duration-200 ${dreamTier2Enabled ? 'left-[22px]' : 'left-0.5'}`} />
|
|
1966
|
+
</div>
|
|
1967
|
+
<span className="flex items-center gap-2 text-[13px] text-text-2">
|
|
1968
|
+
Tier 2 Reflection <HintTip text="Use the agent's LLM to reflect on memories and produce consolidated insights" />
|
|
1969
|
+
</span>
|
|
1970
|
+
</label>
|
|
1971
|
+
</div>
|
|
1972
|
+
)}
|
|
1908
1973
|
</SectionCard>
|
|
1909
1974
|
)}
|
|
1910
1975
|
|
|
@@ -25,6 +25,7 @@ import { ConnectorPlatformIcon, getConnectorPlatformLabel } from '@/components/s
|
|
|
25
25
|
import { copyTextToClipboard } from '@/lib/clipboard'
|
|
26
26
|
import { formatMessageTimestamp } from '@/lib/chat/chat-display'
|
|
27
27
|
import { stripAllInternalMetadata } from '@/lib/strip-internal-metadata'
|
|
28
|
+
import { GroundingPanel } from '@/components/knowledge/grounding-panel'
|
|
28
29
|
|
|
29
30
|
/** Parse delegation-source metadata prefix from system messages */
|
|
30
31
|
const DELEGATION_SOURCE_RE = /^\[delegation-source:([^:]*):([^:]*):([^\]]*)\]/
|
|
@@ -974,6 +975,15 @@ export const MessageBubble = memo(function MessageBubble({ message, assistantNam
|
|
|
974
975
|
</div>
|
|
975
976
|
) : null}
|
|
976
977
|
|
|
978
|
+
{!isUser && (message.citations?.length || message.retrievalTrace?.hits?.length) ? (
|
|
979
|
+
<div className="mt-2 max-w-[85%] md:max-w-[72%]">
|
|
980
|
+
<GroundingPanel
|
|
981
|
+
citations={message.citations}
|
|
982
|
+
retrievalTrace={message.retrievalTrace}
|
|
983
|
+
/>
|
|
984
|
+
</div>
|
|
985
|
+
) : null}
|
|
986
|
+
|
|
977
987
|
{/* Bookmark indicator */}
|
|
978
988
|
{message.bookmarked && (
|
|
979
989
|
<div className={`flex items-center gap-1 mt-1 px-1 ${isUser ? 'justify-end' : ''}`}>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { KnowledgeCitation, KnowledgeRetrievalTrace } from '@/types'
|
|
4
|
+
|
|
5
|
+
function dedupeCitations(citations: KnowledgeCitation[]): KnowledgeCitation[] {
|
|
6
|
+
const seen = new Set<string>()
|
|
7
|
+
const out: KnowledgeCitation[] = []
|
|
8
|
+
for (const citation of citations) {
|
|
9
|
+
const key = `${citation.sourceId}:${citation.chunkId}`
|
|
10
|
+
if (seen.has(key)) continue
|
|
11
|
+
seen.add(key)
|
|
12
|
+
out.push(citation)
|
|
13
|
+
}
|
|
14
|
+
return out
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function GroundingPanel(props: {
|
|
18
|
+
citations?: KnowledgeCitation[]
|
|
19
|
+
retrievalTrace?: KnowledgeRetrievalTrace | null
|
|
20
|
+
title?: string
|
|
21
|
+
compact?: boolean
|
|
22
|
+
className?: string
|
|
23
|
+
}) {
|
|
24
|
+
const explicit = Array.isArray(props.citations) ? dedupeCitations(props.citations) : []
|
|
25
|
+
const fallback = props.retrievalTrace?.hits ? dedupeCitations(props.retrievalTrace.hits) : []
|
|
26
|
+
const items = explicit.length > 0 ? explicit : fallback
|
|
27
|
+
if (items.length === 0) return null
|
|
28
|
+
|
|
29
|
+
const title = props.title || 'Grounding'
|
|
30
|
+
const compact = props.compact === true
|
|
31
|
+
const selected = explicit.length > 0
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<details className={`group rounded-[12px] border border-sky-400/15 bg-sky-400/[0.04] ${props.className || ''}`}>
|
|
35
|
+
<summary className="flex cursor-pointer list-none items-center gap-2 px-3.5 py-2.5 select-none [&::-webkit-details-marker]:hidden">
|
|
36
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="shrink-0 text-sky-300/70 transition-transform group-open:rotate-90">
|
|
37
|
+
<polyline points="9 18 15 12 9 6" />
|
|
38
|
+
</svg>
|
|
39
|
+
<span className="text-[11px] font-600 uppercase tracking-[0.05em] text-sky-200/80">{title}</span>
|
|
40
|
+
<span className="text-[10px] font-mono text-text-3/50">
|
|
41
|
+
{selected ? `${explicit.length} citation${explicit.length === 1 ? '' : 's'}` : `${fallback.length} retrieved`}
|
|
42
|
+
</span>
|
|
43
|
+
{props.retrievalTrace?.selectorStatus && (
|
|
44
|
+
<span className="ml-auto text-[10px] text-text-3/45">
|
|
45
|
+
{props.retrievalTrace.selectorStatus === 'selected'
|
|
46
|
+
? 'selected'
|
|
47
|
+
: props.retrievalTrace.selectorStatus === 'no_match'
|
|
48
|
+
? 'candidates'
|
|
49
|
+
: 'retrieved'}
|
|
50
|
+
</span>
|
|
51
|
+
)}
|
|
52
|
+
</summary>
|
|
53
|
+
|
|
54
|
+
<div className="space-y-2 px-3.5 pb-3 pt-1">
|
|
55
|
+
{props.retrievalTrace?.query && (
|
|
56
|
+
<div className="rounded-[10px] border border-white/[0.06] bg-black/20 px-3 py-2 text-[11px] text-text-3/75">
|
|
57
|
+
Query: <span className="text-text-2">{props.retrievalTrace.query}</span>
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
|
|
61
|
+
{items.map((citation) => (
|
|
62
|
+
<div key={`${citation.sourceId}:${citation.chunkId}`} className="rounded-[10px] border border-white/[0.06] bg-black/15 px-3 py-2.5">
|
|
63
|
+
<div className="flex items-start justify-between gap-3">
|
|
64
|
+
<div className="min-w-0">
|
|
65
|
+
<div className="text-[12px] font-600 text-text-2">{citation.sourceTitle}</div>
|
|
66
|
+
<div className="mt-0.5 text-[10px] text-text-3/60">
|
|
67
|
+
Chunk {citation.chunkIndex + 1} of {citation.chunkCount}
|
|
68
|
+
{citation.sectionLabel ? ` • ${citation.sectionLabel}` : ''}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="shrink-0 text-[10px] font-mono text-text-3/55">
|
|
72
|
+
{citation.score.toFixed(2)}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{citation.whyMatched && (
|
|
77
|
+
<div className="mt-2 text-[11px] text-sky-100/78">{citation.whyMatched}</div>
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
<div className={`mt-2 whitespace-pre-wrap break-words text-text-2/85 ${compact ? 'text-[11px]' : 'text-[12px]'}`}>
|
|
81
|
+
{citation.snippet}
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
{(citation.sourceLabel || citation.sourceUrl) && (
|
|
85
|
+
<div className="mt-2 flex flex-wrap items-center gap-2 text-[10px] text-text-3/55">
|
|
86
|
+
{citation.sourceLabel && <span>{citation.sourceLabel}</span>}
|
|
87
|
+
{citation.sourceUrl && (
|
|
88
|
+
<a href={citation.sourceUrl} target="_blank" rel="noreferrer" className="text-sky-300 hover:underline">
|
|
89
|
+
open source
|
|
90
|
+
</a>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
))}
|
|
96
|
+
</div>
|
|
97
|
+
</details>
|
|
98
|
+
)
|
|
99
|
+
}
|