@swarmclawai/swarmclaw 1.3.4 → 1.3.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 +20 -76
- package/package.json +3 -2
- 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-disabled.test.ts +14 -31
- package/src/lib/server/chat-execution/chat-execution-eval-history.test.ts +11 -34
- package/src/lib/server/chat-execution/chat-execution-grounding.test.ts +108 -0
- package/src/lib/server/chat-execution/chat-execution-session-sync.test.ts +35 -36
- 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 +261 -0
- package/src/lib/server/knowledge-sources.ts +1284 -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/lib/server/test-utils/run-with-temp-data-dir.ts +15 -2
- 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
|
@@ -502,7 +502,12 @@ export function currentArtifact(run: ProtocolRun): ProtocolRunArtifact | null {
|
|
|
502
502
|
return run.artifacts[run.artifacts.length - 1] || null
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
-
export function appendArtifact(
|
|
505
|
+
export function appendArtifact(
|
|
506
|
+
run: ProtocolRun,
|
|
507
|
+
artifact: ProtocolRunArtifact,
|
|
508
|
+
deps?: ProtocolRunDeps,
|
|
509
|
+
options?: { citations?: import('@/types').KnowledgeCitation[] },
|
|
510
|
+
): ProtocolRun {
|
|
506
511
|
const next = persistRun({
|
|
507
512
|
...run,
|
|
508
513
|
artifacts: [...(run.artifacts || []), artifact],
|
|
@@ -518,6 +523,7 @@ export function appendArtifact(run: ProtocolRun, artifact: ProtocolRunArtifact,
|
|
|
518
523
|
phaseId: artifact.phaseId || null,
|
|
519
524
|
artifactId: artifact.id,
|
|
520
525
|
summary: `Emitted ${artifact.kind.replace(/_/g, ' ')}: ${artifact.title}`,
|
|
526
|
+
citations: options?.citations,
|
|
521
527
|
}, deps)
|
|
522
528
|
return next
|
|
523
529
|
}
|
|
@@ -97,7 +97,12 @@ export async function collectResponses(
|
|
|
97
97
|
for (const agentId of current.participantAgentIds) {
|
|
98
98
|
if (responded.has(agentId)) continue
|
|
99
99
|
renewProtocolLease(current.id)
|
|
100
|
-
let response: {
|
|
100
|
+
let response: {
|
|
101
|
+
text: string
|
|
102
|
+
toolEvents: import('@/types').MessageToolEvent[]
|
|
103
|
+
citations?: import('@/types').KnowledgeCitation[]
|
|
104
|
+
retrievalTrace?: import('@/types').KnowledgeRetrievalTrace | null
|
|
105
|
+
}
|
|
101
106
|
try {
|
|
102
107
|
response = await executeAgentTurn({
|
|
103
108
|
run: current,
|
|
@@ -116,7 +121,13 @@ export async function collectResponses(
|
|
|
116
121
|
response = { text: `[Agent error: ${errMsg}]`, toolEvents: [] }
|
|
117
122
|
}
|
|
118
123
|
responded.add(agentId)
|
|
119
|
-
cachedResponses.push({
|
|
124
|
+
cachedResponses.push({
|
|
125
|
+
agentId,
|
|
126
|
+
text: response.text,
|
|
127
|
+
toolEvents: response.toolEvents,
|
|
128
|
+
citations: response.citations,
|
|
129
|
+
retrievalTrace: response.retrievalTrace,
|
|
130
|
+
})
|
|
120
131
|
current = persistRun({
|
|
121
132
|
...current,
|
|
122
133
|
phaseState: {
|
|
@@ -143,6 +154,7 @@ export async function collectResponses(
|
|
|
143
154
|
phaseId: phase.id,
|
|
144
155
|
agentId,
|
|
145
156
|
summary: `Captured a response from ${participantAgents[agentId]?.name || agentId}.`,
|
|
157
|
+
citations: response.citations,
|
|
146
158
|
}, deps)
|
|
147
159
|
}
|
|
148
160
|
}
|
|
@@ -163,6 +175,7 @@ export async function collectResponses(
|
|
|
163
175
|
phaseId: phase.id,
|
|
164
176
|
agentId: response.agentId,
|
|
165
177
|
summary: `Captured an independent response from ${participantAgents[response.agentId]?.name || response.agentId}.`,
|
|
178
|
+
citations: response.citations,
|
|
166
179
|
}, deps)
|
|
167
180
|
}
|
|
168
181
|
current = persistRun({
|
|
@@ -210,7 +223,7 @@ export async function processFacilitatorArtifactPhase(
|
|
|
210
223
|
...(result.toolEvents.length > 0 ? { toolEvents: result.toolEvents } : {}),
|
|
211
224
|
}, deps)
|
|
212
225
|
}
|
|
213
|
-
const updated = appendArtifact(run, artifact, deps)
|
|
226
|
+
const updated = appendArtifact(run, artifact, deps, { citations: result.citations })
|
|
214
227
|
return finishPhase(updated, phase, deps)
|
|
215
228
|
}
|
|
216
229
|
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import type {
|
|
6
6
|
BoardTask,
|
|
7
7
|
Chatroom,
|
|
8
|
+
KnowledgeCitation,
|
|
9
|
+
KnowledgeRetrievalTrace,
|
|
8
10
|
MessageToolEvent,
|
|
9
11
|
ProtocolBranchCase,
|
|
10
12
|
ProtocolPhaseDefinition,
|
|
@@ -72,6 +74,8 @@ export interface UpsertProtocolTemplateInput {
|
|
|
72
74
|
export interface ProtocolAgentTurnResult {
|
|
73
75
|
text: string
|
|
74
76
|
toolEvents: MessageToolEvent[]
|
|
77
|
+
citations?: KnowledgeCitation[]
|
|
78
|
+
retrievalTrace?: KnowledgeRetrievalTrace | null
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
export interface ProtocolRunDeps {
|
|
@@ -1223,9 +1223,14 @@ function stopConnectorHealthMonitor() {
|
|
|
1223
1223
|
|
|
1224
1224
|
function runConsolidationTick() {
|
|
1225
1225
|
import('@/lib/server/memory/memory-consolidation').then(({ runDailyConsolidation, registerConsolidationIdleCallback, registerCompactionIdleCallback }) => {
|
|
1226
|
-
// Wire idle-window callbacks so consolidation and
|
|
1226
|
+
// Wire idle-window callbacks so consolidation, compaction, and dreaming run during quiet periods
|
|
1227
1227
|
registerConsolidationIdleCallback()
|
|
1228
1228
|
registerCompactionIdleCallback()
|
|
1229
|
+
import('@/lib/server/memory/dream-idle-callback').then(({ registerDreamIdleCallback }) => {
|
|
1230
|
+
registerDreamIdleCallback()
|
|
1231
|
+
}).catch((err: unknown) => {
|
|
1232
|
+
log.error(TAG, '[daemon] Dream idle callback registration failed:', errorMessage(err))
|
|
1233
|
+
})
|
|
1229
1234
|
|
|
1230
1235
|
return runDailyConsolidation().then((stats) => {
|
|
1231
1236
|
if (stats.digests > 0 || stats.pruned > 0 || stats.deduped > 0) {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { runWithTempDataDir } from '@/lib/server/test-utils/run-with-temp-data-dir'
|
|
4
|
+
|
|
5
|
+
test('buildRetrievalSummary preserves citation count and dedupes source ids', () => {
|
|
6
|
+
const output = runWithTempDataDir<{
|
|
7
|
+
empty: unknown
|
|
8
|
+
summary: { citationCount: number; sourceIds: string[] } | null
|
|
9
|
+
}>(`
|
|
10
|
+
const ledgerMod = await import('./src/lib/server/runtime/run-ledger.ts')
|
|
11
|
+
const ledger = ledgerMod.default || ledgerMod
|
|
12
|
+
|
|
13
|
+
const summary = ledger.buildRetrievalSummary([
|
|
14
|
+
{ sourceId: 'source-a' },
|
|
15
|
+
{ sourceId: 'source-a' },
|
|
16
|
+
{ sourceId: 'source-b' },
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
console.log(JSON.stringify({
|
|
20
|
+
empty: ledger.buildRetrievalSummary([]),
|
|
21
|
+
summary,
|
|
22
|
+
}))
|
|
23
|
+
`, { prefix: 'swarmclaw-run-ledger-summary-' })
|
|
24
|
+
|
|
25
|
+
assert.equal(output.empty, null)
|
|
26
|
+
assert.deepEqual(output.summary, {
|
|
27
|
+
citationCount: 3,
|
|
28
|
+
sourceIds: ['source-a', 'source-b'],
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('appendPersistedRunEvent stores citations and retrieval traces', () => {
|
|
33
|
+
const output = runWithTempDataDir<{
|
|
34
|
+
eventCount: number
|
|
35
|
+
citationCount: number
|
|
36
|
+
selectorStatus: string | null
|
|
37
|
+
sourceIds: string[]
|
|
38
|
+
}>(`
|
|
39
|
+
const ledgerMod = await import('./src/lib/server/runtime/run-ledger.ts')
|
|
40
|
+
const storageMod = await import('./src/lib/server/storage.ts')
|
|
41
|
+
const ledger = ledgerMod.default || ledgerMod
|
|
42
|
+
const storage = storageMod.default || storageMod
|
|
43
|
+
|
|
44
|
+
ledger.persistRun({
|
|
45
|
+
id: 'run-grounded',
|
|
46
|
+
sessionId: 'session-1',
|
|
47
|
+
source: 'chat',
|
|
48
|
+
internal: false,
|
|
49
|
+
mode: 'followup',
|
|
50
|
+
status: 'completed',
|
|
51
|
+
messagePreview: 'grounded response',
|
|
52
|
+
queuedAt: 1,
|
|
53
|
+
retrievalSummary: null,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
ledger.appendPersistedRunEvent({
|
|
57
|
+
runId: 'run-grounded',
|
|
58
|
+
sessionId: 'session-1',
|
|
59
|
+
phase: 'status',
|
|
60
|
+
status: 'completed',
|
|
61
|
+
citations: [{
|
|
62
|
+
sourceId: 'source-a',
|
|
63
|
+
sourceTitle: 'Gateway Runbook',
|
|
64
|
+
sourceKind: 'manual',
|
|
65
|
+
sourceUrl: null,
|
|
66
|
+
sourceLabel: null,
|
|
67
|
+
chunkId: 'chunk-1',
|
|
68
|
+
chunkIndex: 0,
|
|
69
|
+
chunkCount: 1,
|
|
70
|
+
charStart: 0,
|
|
71
|
+
charEnd: 42,
|
|
72
|
+
sectionLabel: null,
|
|
73
|
+
snippet: 'Use blue green deployment for gateway changes.',
|
|
74
|
+
whyMatched: 'Matched query terms: gateway, deployment',
|
|
75
|
+
score: 0.91,
|
|
76
|
+
}],
|
|
77
|
+
retrievalTrace: {
|
|
78
|
+
query: 'gateway deployment',
|
|
79
|
+
scope: 'source_knowledge',
|
|
80
|
+
hits: [{
|
|
81
|
+
sourceId: 'source-a',
|
|
82
|
+
sourceTitle: 'Gateway Runbook',
|
|
83
|
+
sourceKind: 'manual',
|
|
84
|
+
sourceUrl: null,
|
|
85
|
+
sourceLabel: null,
|
|
86
|
+
chunkId: 'chunk-1',
|
|
87
|
+
chunkIndex: 0,
|
|
88
|
+
chunkCount: 1,
|
|
89
|
+
charStart: 0,
|
|
90
|
+
charEnd: 42,
|
|
91
|
+
sectionLabel: null,
|
|
92
|
+
snippet: 'Use blue green deployment for gateway changes.',
|
|
93
|
+
whyMatched: 'Matched query terms: gateway, deployment',
|
|
94
|
+
score: 0.91,
|
|
95
|
+
}],
|
|
96
|
+
retrievedAt: 123,
|
|
97
|
+
selectorStatus: 'selected',
|
|
98
|
+
},
|
|
99
|
+
event: {
|
|
100
|
+
t: 'md',
|
|
101
|
+
text: '{"run":{"status":"completed"}}',
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const events = Object.values(storage.loadRuntimeRunEvents())
|
|
106
|
+
const stored = events[0]
|
|
107
|
+
|
|
108
|
+
console.log(JSON.stringify({
|
|
109
|
+
eventCount: events.length,
|
|
110
|
+
citationCount: Array.isArray(stored?.citations) ? stored.citations.length : 0,
|
|
111
|
+
selectorStatus: stored?.retrievalTrace?.selectorStatus || null,
|
|
112
|
+
sourceIds: Array.isArray(stored?.citations) ? stored.citations.map((citation) => citation.sourceId) : [],
|
|
113
|
+
}))
|
|
114
|
+
`, { prefix: 'swarmclaw-run-ledger-events-' })
|
|
115
|
+
|
|
116
|
+
assert.equal(output.eventCount, 1)
|
|
117
|
+
assert.equal(output.citationCount, 1)
|
|
118
|
+
assert.equal(output.selectorStatus, 'selected')
|
|
119
|
+
assert.deepEqual(output.sourceIds, ['source-a'])
|
|
120
|
+
})
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { genId } from '@/lib/id'
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
KnowledgeCitation,
|
|
4
|
+
KnowledgeRetrievalTrace,
|
|
5
|
+
RunEventRecord,
|
|
6
|
+
SessionRunRecord,
|
|
7
|
+
SessionRunStatus,
|
|
8
|
+
SSEEvent,
|
|
9
|
+
} from '@/types'
|
|
3
10
|
import {
|
|
4
11
|
deleteRuntimeRun,
|
|
5
12
|
deleteRuntimeRunEvent,
|
|
@@ -26,6 +33,21 @@ function now(): number {
|
|
|
26
33
|
return Date.now()
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
export function buildRetrievalSummary(
|
|
37
|
+
citations?: KnowledgeCitation[] | null,
|
|
38
|
+
): SessionRunRecord['retrievalSummary'] {
|
|
39
|
+
if (!Array.isArray(citations) || citations.length === 0) return null
|
|
40
|
+
const sourceIds = Array.from(new Set(
|
|
41
|
+
citations
|
|
42
|
+
.map((citation) => (typeof citation.sourceId === 'string' ? citation.sourceId.trim() : ''))
|
|
43
|
+
.filter(Boolean),
|
|
44
|
+
))
|
|
45
|
+
return {
|
|
46
|
+
citationCount: citations.length,
|
|
47
|
+
sourceIds,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
function summarizeEvent(event: SSEEvent): string | undefined {
|
|
30
52
|
const raw = event.text || event.toolOutput || event.toolInput || event.toolName || ''
|
|
31
53
|
if (!raw) return undefined
|
|
@@ -82,6 +104,8 @@ export function appendPersistedRunEvent(input: {
|
|
|
82
104
|
event: SSEEvent
|
|
83
105
|
timestamp?: number
|
|
84
106
|
summary?: string
|
|
107
|
+
citations?: KnowledgeCitation[]
|
|
108
|
+
retrievalTrace?: KnowledgeRetrievalTrace | null
|
|
85
109
|
}): RunEventRecord {
|
|
86
110
|
const timestamp = typeof input.timestamp === 'number' && Number.isFinite(input.timestamp)
|
|
87
111
|
? Math.trunc(input.timestamp)
|
|
@@ -99,6 +123,8 @@ export function appendPersistedRunEvent(input: {
|
|
|
99
123
|
status: input.status,
|
|
100
124
|
summary: input.summary || summarizeEvent(input.event),
|
|
101
125
|
event: input.event,
|
|
126
|
+
citations: input.citations,
|
|
127
|
+
retrievalTrace: input.retrievalTrace || undefined,
|
|
102
128
|
}
|
|
103
129
|
upsertRuntimeRunEvent(record.id, record)
|
|
104
130
|
return record
|
|
@@ -4,6 +4,7 @@ import { isInternalHeartbeatRun } from '@/lib/server/runtime/heartbeat-source'
|
|
|
4
4
|
import { notify } from '@/lib/server/ws-hub'
|
|
5
5
|
import { errorMessage } from '@/lib/shared-utils'
|
|
6
6
|
import { handleMainLoopRunResult } from '@/lib/server/agents/main-agent-loop'
|
|
7
|
+
import { buildRetrievalSummary } from '@/lib/server/runtime/run-ledger'
|
|
7
8
|
|
|
8
9
|
import {
|
|
9
10
|
clearDeferredDrain,
|
|
@@ -119,6 +120,7 @@ export async function drainExecution(
|
|
|
119
120
|
next.run.endedAt = next.run.endedAt || now()
|
|
120
121
|
next.run.error = aborted ? (next.run.error || 'Cancelled') : result.error
|
|
121
122
|
next.run.resultPreview = result.text?.slice(0, 280)
|
|
123
|
+
next.run.retrievalSummary = buildRetrievalSummary(result.citations)
|
|
122
124
|
if (typeof result.inputTokens === 'number') next.run.totalInputTokens = result.inputTokens
|
|
123
125
|
if (typeof result.outputTokens === 'number') next.run.totalOutputTokens = result.outputTokens
|
|
124
126
|
if (typeof result.estimatedCost === 'number') next.run.estimatedCost = result.estimatedCost
|
|
@@ -127,6 +129,9 @@ export async function drainExecution(
|
|
|
127
129
|
persisted: result.persisted,
|
|
128
130
|
hasText: !!result.text,
|
|
129
131
|
error: next.run.error || null,
|
|
132
|
+
}, {
|
|
133
|
+
citations: result.citations,
|
|
134
|
+
retrievalTrace: result.retrievalTrace,
|
|
130
135
|
})
|
|
131
136
|
log.info('session-run', `Run finished ${next.run.id}`, {
|
|
132
137
|
sessionId: next.run.sessionId,
|
|
@@ -113,6 +113,8 @@ export function persistEventForRun(entry: SessionRunQueueEntry, event: SSEEvent,
|
|
|
113
113
|
phase?: RunEventRecord['phase']
|
|
114
114
|
status?: SessionRunStatus
|
|
115
115
|
summary?: string
|
|
116
|
+
citations?: import('@/types').KnowledgeCitation[]
|
|
117
|
+
retrievalTrace?: import('@/types').KnowledgeRetrievalTrace | null
|
|
116
118
|
}): void {
|
|
117
119
|
if (!shouldPersistRunEvent(event)) return
|
|
118
120
|
appendPersistedRunEvent({
|
|
@@ -125,6 +127,8 @@ export function persistEventForRun(entry: SessionRunQueueEntry, event: SSEEvent,
|
|
|
125
127
|
phase: opts?.phase || 'event',
|
|
126
128
|
status: opts?.status,
|
|
127
129
|
summary: opts?.summary,
|
|
130
|
+
citations: opts?.citations,
|
|
131
|
+
retrievalTrace: opts?.retrievalTrace || null,
|
|
128
132
|
event,
|
|
129
133
|
})
|
|
130
134
|
}
|
|
@@ -149,7 +153,15 @@ export function emitToSubscribers(entry: SessionRunQueueEntry, event: SSEEvent)
|
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
export function emitRunMeta(
|
|
156
|
+
export function emitRunMeta(
|
|
157
|
+
entry: SessionRunQueueEntry,
|
|
158
|
+
status: SessionRunStatus,
|
|
159
|
+
extra?: Record<string, unknown>,
|
|
160
|
+
persist?: {
|
|
161
|
+
citations?: import('@/types').KnowledgeCitation[]
|
|
162
|
+
retrievalTrace?: import('@/types').KnowledgeRetrievalTrace | null
|
|
163
|
+
},
|
|
164
|
+
) {
|
|
153
165
|
const event: SSEEvent = {
|
|
154
166
|
t: 'md',
|
|
155
167
|
text: JSON.stringify({
|
|
@@ -163,7 +175,12 @@ export function emitRunMeta(entry: SessionRunQueueEntry, status: SessionRunStatu
|
|
|
163
175
|
},
|
|
164
176
|
}),
|
|
165
177
|
}
|
|
166
|
-
persistEventForRun(entry, event, {
|
|
178
|
+
persistEventForRun(entry, event, {
|
|
179
|
+
phase: 'status',
|
|
180
|
+
status,
|
|
181
|
+
citations: persist?.citations,
|
|
182
|
+
retrievalTrace: persist?.retrievalTrace || null,
|
|
183
|
+
})
|
|
167
184
|
for (const send of entry.onEvents) {
|
|
168
185
|
try {
|
|
169
186
|
send(event)
|
|
@@ -507,6 +507,11 @@ function normalizeStoredRecordInner(
|
|
|
507
507
|
agent.delegationEnabled = false
|
|
508
508
|
agent.heartbeatEnabled = false
|
|
509
509
|
}
|
|
510
|
+
// Dreaming defaults
|
|
511
|
+
if (agent.dreamEnabled === undefined) agent.dreamEnabled = false
|
|
512
|
+
if (agent.dreamConfig === undefined) agent.dreamConfig = null
|
|
513
|
+
if (agent.lastDreamAt === undefined) agent.lastDreamAt = null
|
|
514
|
+
if (typeof agent.dreamCycleCount !== 'number') agent.dreamCycleCount = 0
|
|
510
515
|
// Persisted spend rollup defaults
|
|
511
516
|
if (typeof agent.spentMonthlyCents !== 'number') agent.spentMonthlyCents = 0
|
|
512
517
|
if (typeof agent.spentDailyCents !== 'number') agent.spentDailyCents = 0
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
ExternalAgentRuntime,
|
|
23
23
|
GatewayProfile,
|
|
24
24
|
GuardianCheckpoint,
|
|
25
|
+
KnowledgeSource,
|
|
25
26
|
LearnedSkill,
|
|
26
27
|
Message,
|
|
27
28
|
ProtocolTemplate,
|
|
@@ -126,6 +127,7 @@ const COLLECTIONS = [
|
|
|
126
127
|
'connectors',
|
|
127
128
|
'documents',
|
|
128
129
|
'document_revisions',
|
|
130
|
+
'knowledge_sources',
|
|
129
131
|
'webhooks',
|
|
130
132
|
'model_overrides',
|
|
131
133
|
'mcp_servers',
|
|
@@ -1429,6 +1431,19 @@ const documentRevisionsStore = createCollectionStore('document_revisions')
|
|
|
1429
1431
|
export const loadDocumentRevisions = documentRevisionsStore.load
|
|
1430
1432
|
export const upsertDocumentRevision = documentRevisionsStore.upsert
|
|
1431
1433
|
|
|
1434
|
+
// --- Knowledge Sources ---
|
|
1435
|
+
const knowledgeSourcesStore = createCollectionStore('knowledge_sources')
|
|
1436
|
+
export const loadKnowledgeSources = knowledgeSourcesStore.load as () => Record<string, KnowledgeSource>
|
|
1437
|
+
export const saveKnowledgeSources = knowledgeSourcesStore.save as (data: Record<string, KnowledgeSource>) => void
|
|
1438
|
+
export const loadKnowledgeSource = knowledgeSourcesStore.loadItem as (id: string) => KnowledgeSource | null
|
|
1439
|
+
export const upsertKnowledgeSource = knowledgeSourcesStore.upsert as (id: string, value: KnowledgeSource) => void
|
|
1440
|
+
export const upsertKnowledgeSources = knowledgeSourcesStore.upsertMany
|
|
1441
|
+
export const patchKnowledgeSource = knowledgeSourcesStore.patch as (
|
|
1442
|
+
id: string,
|
|
1443
|
+
updater: (current: KnowledgeSource | null) => KnowledgeSource | null,
|
|
1444
|
+
) => KnowledgeSource | null
|
|
1445
|
+
export const deleteKnowledgeSource = knowledgeSourcesStore.deleteItem
|
|
1446
|
+
|
|
1432
1447
|
// --- Webhooks ---
|
|
1433
1448
|
const webhooksStore = createCollectionStore('webhooks')
|
|
1434
1449
|
export const loadWebhooks = webhooksStore.load
|
|
@@ -10,19 +10,32 @@ export function runWithTempDataDir<T = unknown>(
|
|
|
10
10
|
script: string,
|
|
11
11
|
options: {
|
|
12
12
|
prefix?: string
|
|
13
|
+
dataDir?: string
|
|
14
|
+
workspaceDir?: string
|
|
15
|
+
browserProfilesDir?: string
|
|
13
16
|
} = {},
|
|
14
17
|
): T {
|
|
15
18
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), options.prefix || 'swarmclaw-test-'))
|
|
16
|
-
const
|
|
19
|
+
const resolveTempPath = (value: string | undefined, fallback: string): string =>
|
|
20
|
+
path.isAbsolute(value || '') ? String(value) : path.join(tempDir, value || fallback)
|
|
21
|
+
const dataDir = resolveTempPath(options.dataDir, '')
|
|
22
|
+
const workspaceDir = resolveTempPath(options.workspaceDir, 'workspace')
|
|
23
|
+
const browserProfilesDir = options.browserProfilesDir
|
|
24
|
+
? resolveTempPath(options.browserProfilesDir, 'browser-profiles')
|
|
25
|
+
: null
|
|
26
|
+
|
|
27
|
+
fs.mkdirSync(dataDir, { recursive: true })
|
|
17
28
|
fs.mkdirSync(workspaceDir, { recursive: true })
|
|
29
|
+
if (browserProfilesDir) fs.mkdirSync(browserProfilesDir, { recursive: true })
|
|
18
30
|
|
|
19
31
|
try {
|
|
20
32
|
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
|
|
21
33
|
cwd: repoRoot,
|
|
22
34
|
env: {
|
|
23
35
|
...process.env,
|
|
24
|
-
DATA_DIR:
|
|
36
|
+
DATA_DIR: dataDir,
|
|
25
37
|
WORKSPACE_DIR: workspaceDir,
|
|
38
|
+
...(browserProfilesDir ? { BROWSER_PROFILES_DIR: browserProfilesDir } : {}),
|
|
26
39
|
},
|
|
27
40
|
encoding: 'utf-8',
|
|
28
41
|
})
|
|
@@ -71,6 +71,8 @@ export interface UiSlice {
|
|
|
71
71
|
setKnowledgeSheetOpen: (open: boolean) => void
|
|
72
72
|
editingKnowledgeId: string | null
|
|
73
73
|
setEditingKnowledgeId: (id: string | null) => void
|
|
74
|
+
selectedKnowledgeSourceId: string | null
|
|
75
|
+
setSelectedKnowledgeSourceId: (id: string | null) => void
|
|
74
76
|
knowledgeRefreshKey: number
|
|
75
77
|
triggerKnowledgeRefresh: () => void
|
|
76
78
|
extensionSheetOpen: boolean
|
|
@@ -169,6 +171,8 @@ export const createUiSlice: StateCreator<AppState, [], [], UiSlice> = (set, get)
|
|
|
169
171
|
setKnowledgeSheetOpen: (open) => set({ knowledgeSheetOpen: open }),
|
|
170
172
|
editingKnowledgeId: null,
|
|
171
173
|
setEditingKnowledgeId: (id) => set({ editingKnowledgeId: id }),
|
|
174
|
+
selectedKnowledgeSourceId: null,
|
|
175
|
+
setSelectedKnowledgeSourceId: (id) => set({ selectedKnowledgeSourceId: id }),
|
|
172
176
|
knowledgeRefreshKey: 0,
|
|
173
177
|
triggerKnowledgeRefresh: () => set((s) => ({ knowledgeRefreshKey: s.knowledgeRefreshKey + 1 })),
|
|
174
178
|
extensionSheetOpen: false,
|
package/src/types/agent.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ProviderId, ProviderType, OllamaMode } from './provider'
|
|
2
2
|
import type { SessionResetMode, IdentityContinuityState } from './session'
|
|
3
3
|
import type { SkillAllowlistMode } from './skill'
|
|
4
|
+
import type { DreamConfig } from './dream'
|
|
4
5
|
|
|
5
6
|
// --- Agent / Delegation ---
|
|
6
7
|
|
|
@@ -164,6 +165,12 @@ export interface Agent {
|
|
|
164
165
|
lastSpendRollupAt?: number
|
|
165
166
|
maxFollowupChain?: number
|
|
166
167
|
|
|
168
|
+
// Dreaming (idle-time memory consolidation)
|
|
169
|
+
dreamEnabled?: boolean
|
|
170
|
+
dreamConfig?: Partial<DreamConfig> | null
|
|
171
|
+
lastDreamAt?: number | null
|
|
172
|
+
dreamCycleCount?: number
|
|
173
|
+
|
|
167
174
|
// Orchestrator Mode
|
|
168
175
|
orchestratorEnabled?: boolean
|
|
169
176
|
orchestratorMission?: string
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// --- Dreaming (idle-time memory consolidation) ---
|
|
2
|
+
|
|
3
|
+
export type DreamStatus = 'pending' | 'running' | 'completed' | 'failed'
|
|
4
|
+
export type DreamTrigger = 'idle' | 'manual'
|
|
5
|
+
|
|
6
|
+
export interface DreamCycleResult {
|
|
7
|
+
decayed: number
|
|
8
|
+
pruned: number
|
|
9
|
+
promoted: number
|
|
10
|
+
deduped: number
|
|
11
|
+
consolidated: number
|
|
12
|
+
reflections: string[]
|
|
13
|
+
memoriesReviewed: number
|
|
14
|
+
durationMs: number
|
|
15
|
+
errors: string[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DreamCycle {
|
|
19
|
+
id: string
|
|
20
|
+
agentId: string
|
|
21
|
+
status: DreamStatus
|
|
22
|
+
trigger: DreamTrigger
|
|
23
|
+
startedAt: number
|
|
24
|
+
completedAt?: number | null
|
|
25
|
+
result?: DreamCycleResult | null
|
|
26
|
+
error?: string | null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DreamConfig {
|
|
30
|
+
enabled: boolean
|
|
31
|
+
cooldownMinutes: number
|
|
32
|
+
decayAgeDays: number
|
|
33
|
+
pruneThresholdDays: number
|
|
34
|
+
tier2Enabled: boolean
|
|
35
|
+
tier2MaxMemories: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const DEFAULT_DREAM_CONFIG: DreamConfig = {
|
|
39
|
+
enabled: true,
|
|
40
|
+
cooldownMinutes: 360,
|
|
41
|
+
decayAgeDays: 30,
|
|
42
|
+
pruneThresholdDays: 90,
|
|
43
|
+
tier2Enabled: true,
|
|
44
|
+
tier2MaxMemories: 50,
|
|
45
|
+
}
|
package/src/types/index.ts
CHANGED
package/src/types/message.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MessageSource } from './connector'
|
|
2
|
+
import type { KnowledgeCitation, KnowledgeRetrievalTrace } from './misc'
|
|
2
3
|
|
|
3
4
|
export interface MessageToolEvent {
|
|
4
5
|
name: string
|
|
@@ -53,4 +54,6 @@ export interface Message {
|
|
|
53
54
|
runId?: string
|
|
54
55
|
/** Cached turn semantics used for routing, delegation, and reflection. */
|
|
55
56
|
semantics?: MessageSemanticsSummary
|
|
57
|
+
citations?: KnowledgeCitation[]
|
|
58
|
+
retrievalTrace?: KnowledgeRetrievalTrace | null
|
|
56
59
|
}
|
package/src/types/misc.ts
CHANGED
|
@@ -469,6 +469,137 @@ export interface MemoryEntry {
|
|
|
469
469
|
updatedAt: number
|
|
470
470
|
}
|
|
471
471
|
|
|
472
|
+
export type KnowledgeSourceKind = 'manual' | 'file' | 'url'
|
|
473
|
+
export type KnowledgeSyncStatus = 'ready' | 'syncing' | 'error'
|
|
474
|
+
export type KnowledgeHygieneActionKind = 'sync' | 'reindex' | 'archive' | 'restore' | 'supersede'
|
|
475
|
+
export type KnowledgeHygieneFindingKind = 'stale' | 'duplicate' | 'overlap' | 'broken' | 'archived' | 'superseded'
|
|
476
|
+
|
|
477
|
+
export interface KnowledgeCitation {
|
|
478
|
+
sourceId: string
|
|
479
|
+
sourceTitle: string
|
|
480
|
+
sourceKind: KnowledgeSourceKind
|
|
481
|
+
sourceUrl?: string | null
|
|
482
|
+
sourceLabel?: string | null
|
|
483
|
+
chunkId: string
|
|
484
|
+
chunkIndex: number
|
|
485
|
+
chunkCount: number
|
|
486
|
+
charStart: number
|
|
487
|
+
charEnd: number
|
|
488
|
+
sectionLabel?: string | null
|
|
489
|
+
snippet: string
|
|
490
|
+
whyMatched?: string | null
|
|
491
|
+
score: number
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export interface KnowledgeRetrievalTrace {
|
|
495
|
+
query: string
|
|
496
|
+
scope: 'source_knowledge'
|
|
497
|
+
hits: KnowledgeCitation[]
|
|
498
|
+
retrievedAt: number
|
|
499
|
+
selectorStatus?: 'not_run' | 'selected' | 'no_match'
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export interface KnowledgeSource {
|
|
503
|
+
id: string
|
|
504
|
+
kind: KnowledgeSourceKind
|
|
505
|
+
title: string
|
|
506
|
+
content?: string | null
|
|
507
|
+
sourceLabel?: string | null
|
|
508
|
+
sourceUrl?: string | null
|
|
509
|
+
sourcePath?: string | null
|
|
510
|
+
sourceHash?: string | null
|
|
511
|
+
scope: 'global' | 'agent'
|
|
512
|
+
agentIds: string[]
|
|
513
|
+
tags: string[]
|
|
514
|
+
syncStatus: KnowledgeSyncStatus
|
|
515
|
+
lastIndexedAt?: number | null
|
|
516
|
+
lastSyncedAt?: number | null
|
|
517
|
+
lastError?: string | null
|
|
518
|
+
archivedAt?: number | null
|
|
519
|
+
archivedReason?: string | null
|
|
520
|
+
duplicateOfSourceId?: string | null
|
|
521
|
+
supersededBySourceId?: string | null
|
|
522
|
+
maintenanceUpdatedAt?: number | null
|
|
523
|
+
maintenanceNotes?: string | null
|
|
524
|
+
nextSyncAt?: number | null
|
|
525
|
+
lastAutoSyncAt?: number | null
|
|
526
|
+
chunkCount: number
|
|
527
|
+
contentLength: number
|
|
528
|
+
createdAt: number
|
|
529
|
+
updatedAt: number
|
|
530
|
+
metadata?: Record<string, unknown>
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export interface KnowledgeSearchHit {
|
|
534
|
+
id: string
|
|
535
|
+
sourceId: string
|
|
536
|
+
sourceTitle: string
|
|
537
|
+
sourceKind: KnowledgeSourceKind
|
|
538
|
+
sourceUrl?: string | null
|
|
539
|
+
sourceLabel?: string | null
|
|
540
|
+
scope: 'global' | 'agent'
|
|
541
|
+
agentIds: string[]
|
|
542
|
+
tags: string[]
|
|
543
|
+
syncStatus: KnowledgeSyncStatus
|
|
544
|
+
stale: boolean
|
|
545
|
+
title: string
|
|
546
|
+
snippet: string
|
|
547
|
+
content: string
|
|
548
|
+
chunkIndex: number
|
|
549
|
+
chunkCount: number
|
|
550
|
+
charStart: number
|
|
551
|
+
charEnd: number
|
|
552
|
+
sectionLabel?: string | null
|
|
553
|
+
score: number
|
|
554
|
+
whyMatched?: string | null
|
|
555
|
+
createdAt: number
|
|
556
|
+
updatedAt: number
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export interface KnowledgeSourceSummary extends KnowledgeSource {
|
|
560
|
+
stale: boolean
|
|
561
|
+
topSnippet?: string | null
|
|
562
|
+
matchCount?: number
|
|
563
|
+
topScore?: number
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export interface KnowledgeSourceDetail {
|
|
567
|
+
source: KnowledgeSourceSummary
|
|
568
|
+
chunks: MemoryEntry[]
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export interface KnowledgeHygieneFinding {
|
|
572
|
+
kind: KnowledgeHygieneFindingKind
|
|
573
|
+
sourceId: string
|
|
574
|
+
sourceTitle: string
|
|
575
|
+
relatedSourceId?: string | null
|
|
576
|
+
relatedSourceTitle?: string | null
|
|
577
|
+
detail: string
|
|
578
|
+
createdAt: number
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export interface KnowledgeHygieneAction {
|
|
582
|
+
kind: KnowledgeHygieneActionKind
|
|
583
|
+
sourceId: string
|
|
584
|
+
relatedSourceId?: string | null
|
|
585
|
+
summary: string
|
|
586
|
+
createdAt: number
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
export interface KnowledgeHygieneSummary {
|
|
590
|
+
scannedAt: number
|
|
591
|
+
counts: {
|
|
592
|
+
stale: number
|
|
593
|
+
duplicate: number
|
|
594
|
+
overlap: number
|
|
595
|
+
broken: number
|
|
596
|
+
archived: number
|
|
597
|
+
superseded: number
|
|
598
|
+
}
|
|
599
|
+
findings: KnowledgeHygieneFinding[]
|
|
600
|
+
recentActions: KnowledgeHygieneAction[]
|
|
601
|
+
}
|
|
602
|
+
|
|
472
603
|
// --- MCP Servers ---
|
|
473
604
|
|
|
474
605
|
export type McpTransport = 'stdio' | 'sse' | 'streamable-http'
|