@swarmclawai/swarmclaw 0.6.8 → 0.7.0
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 +70 -45
- package/next.config.ts +31 -6
- package/package.json +3 -2
- package/src/app/api/agents/[id]/thread/route.ts +1 -0
- package/src/app/api/agents/route.ts +18 -5
- package/src/app/api/approvals/route.ts +22 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
- package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
- package/src/app/api/memory/route.ts +36 -5
- package/src/app/api/notifications/route.ts +3 -0
- package/src/app/api/plugins/install/route.ts +57 -5
- package/src/app/api/plugins/marketplace/route.ts +73 -22
- package/src/app/api/plugins/route.ts +61 -1
- package/src/app/api/plugins/ui/route.ts +34 -0
- package/src/app/api/settings/route.ts +62 -0
- package/src/app/api/setup/doctor/route.ts +22 -5
- package/src/app/api/tasks/[id]/approve/route.ts +4 -3
- package/src/app/api/tasks/[id]/route.ts +11 -3
- package/src/app/api/tasks/route.ts +8 -2
- package/src/app/globals.css +27 -0
- package/src/app/page.tsx +10 -5
- package/src/cli/index.js +13 -0
- package/src/components/activity/activity-feed.tsx +9 -2
- package/src/components/agents/agent-avatar.tsx +5 -1
- package/src/components/agents/agent-card.tsx +55 -9
- package/src/components/agents/agent-sheet.tsx +86 -29
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/auth/access-key-gate.tsx +63 -54
- package/src/components/auth/user-picker.tsx +37 -32
- package/src/components/chat/chat-area.tsx +11 -0
- package/src/components/chat/chat-header.tsx +69 -25
- package/src/components/chat/chat-tool-toggles.tsx +2 -2
- package/src/components/chat/code-block.tsx +3 -1
- package/src/components/chat/exec-approval-card.tsx +8 -1
- package/src/components/chat/message-bubble.tsx +164 -4
- package/src/components/chat/message-list.tsx +30 -4
- package/src/components/chat/session-approval-card.tsx +80 -0
- package/src/components/chat/streaming-bubble.tsx +6 -5
- package/src/components/chat/thinking-indicator.tsx +48 -12
- package/src/components/chat/tool-request-banner.tsx +39 -20
- package/src/components/chatrooms/chatroom-list.tsx +11 -4
- package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
- package/src/components/connectors/connector-list.tsx +33 -11
- package/src/components/connectors/connector-sheet.tsx +29 -6
- package/src/components/home/home-view.tsx +20 -14
- package/src/components/input/chat-input.tsx +22 -1
- package/src/components/knowledge/knowledge-list.tsx +17 -18
- package/src/components/knowledge/knowledge-sheet.tsx +9 -5
- package/src/components/layout/app-layout.tsx +73 -21
- package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
- package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
- package/src/components/memory/memory-list.tsx +20 -13
- package/src/components/plugins/plugin-list.tsx +213 -59
- package/src/components/plugins/plugin-sheet.tsx +119 -24
- package/src/components/projects/project-list.tsx +17 -9
- package/src/components/providers/provider-list.tsx +21 -6
- package/src/components/providers/provider-sheet.tsx +42 -25
- package/src/components/runs/run-list.tsx +17 -13
- package/src/components/schedules/schedule-card.tsx +10 -3
- package/src/components/schedules/schedule-list.tsx +2 -2
- package/src/components/schedules/schedule-sheet.tsx +19 -7
- package/src/components/secrets/secret-sheet.tsx +7 -2
- package/src/components/secrets/secrets-list.tsx +18 -5
- package/src/components/sessions/new-session-sheet.tsx +183 -376
- package/src/components/sessions/session-card.tsx +10 -2
- package/src/components/settings/gateway-connection-panel.tsx +9 -8
- package/src/components/shared/command-palette.tsx +13 -5
- package/src/components/shared/empty-state.tsx +20 -8
- package/src/components/shared/notification-center.tsx +134 -86
- package/src/components/shared/profile-sheet.tsx +4 -0
- package/src/components/shared/settings/plugin-manager.tsx +360 -135
- package/src/components/shared/settings/section-capability-policy.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +144 -0
- package/src/components/skills/clawhub-browser.tsx +1 -0
- package/src/components/skills/skill-list.tsx +31 -12
- package/src/components/skills/skill-sheet.tsx +20 -7
- package/src/components/tasks/approvals-panel.tsx +170 -66
- package/src/components/tasks/task-board.tsx +20 -12
- package/src/components/tasks/task-card.tsx +21 -7
- package/src/components/tasks/task-column.tsx +4 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +130 -1
- package/src/components/ui/dialog.tsx +1 -0
- package/src/components/ui/sheet.tsx +1 -0
- package/src/components/usage/metrics-dashboard.tsx +66 -64
- package/src/components/wallets/wallet-panel.tsx +65 -41
- package/src/components/wallets/wallet-section.tsx +9 -3
- package/src/components/webhooks/webhook-list.tsx +21 -12
- package/src/components/webhooks/webhook-sheet.tsx +13 -3
- package/src/lib/approval-display.test.ts +45 -0
- package/src/lib/approval-display.ts +62 -0
- package/src/lib/clipboard.ts +38 -0
- package/src/lib/memory.ts +8 -0
- package/src/lib/providers/claude-cli.ts +5 -3
- package/src/lib/providers/index.ts +67 -21
- package/src/lib/runtime-loop.ts +3 -2
- package/src/lib/server/approvals.ts +150 -0
- package/src/lib/server/chat-execution.ts +223 -62
- package/src/lib/server/clawhub-client.ts +82 -6
- package/src/lib/server/connectors/manager.ts +27 -1
- package/src/lib/server/cost.test.ts +73 -0
- package/src/lib/server/cost.ts +165 -34
- package/src/lib/server/daemon-state.ts +42 -0
- package/src/lib/server/data-dir.ts +18 -1
- package/src/lib/server/integrity-monitor.ts +208 -0
- package/src/lib/server/llm-response-cache.test.ts +102 -0
- package/src/lib/server/llm-response-cache.ts +227 -0
- package/src/lib/server/main-agent-loop.ts +1 -1
- package/src/lib/server/main-session.ts +6 -3
- package/src/lib/server/mcp-conformance.test.ts +18 -0
- package/src/lib/server/mcp-conformance.ts +233 -0
- package/src/lib/server/memory-db.ts +180 -17
- package/src/lib/server/memory-retrieval.test.ts +56 -0
- package/src/lib/server/orchestrator-lg.ts +4 -1
- package/src/lib/server/orchestrator.ts +4 -3
- package/src/lib/server/plugins.ts +650 -142
- package/src/lib/server/process-manager.ts +18 -0
- package/src/lib/server/queue.ts +253 -11
- package/src/lib/server/runtime-settings.ts +9 -0
- package/src/lib/server/session-run-manager.test.ts +23 -0
- package/src/lib/server/session-run-manager.ts +11 -1
- package/src/lib/server/session-tools/canvas.ts +85 -50
- package/src/lib/server/session-tools/chatroom.ts +130 -127
- package/src/lib/server/session-tools/connector.ts +233 -454
- package/src/lib/server/session-tools/context-mgmt.ts +87 -105
- package/src/lib/server/session-tools/crud.ts +84 -7
- package/src/lib/server/session-tools/delegate.ts +351 -752
- package/src/lib/server/session-tools/discovery.ts +198 -0
- package/src/lib/server/session-tools/edit_file.ts +82 -0
- package/src/lib/server/session-tools/file-send.test.ts +39 -0
- package/src/lib/server/session-tools/file.ts +257 -425
- package/src/lib/server/session-tools/git.ts +87 -47
- package/src/lib/server/session-tools/http.ts +85 -33
- package/src/lib/server/session-tools/index.ts +205 -160
- package/src/lib/server/session-tools/memory.ts +152 -265
- package/src/lib/server/session-tools/monitor.ts +126 -0
- package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
- package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
- package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
- package/src/lib/server/session-tools/platform.ts +86 -0
- package/src/lib/server/session-tools/plugin-creator.ts +239 -0
- package/src/lib/server/session-tools/sample-ui.ts +97 -0
- package/src/lib/server/session-tools/sandbox.ts +175 -148
- package/src/lib/server/session-tools/schedule.ts +66 -31
- package/src/lib/server/session-tools/session-info.ts +104 -410
- package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
- package/src/lib/server/session-tools/shell.ts +171 -143
- package/src/lib/server/session-tools/subagent.ts +77 -77
- package/src/lib/server/session-tools/wallet.ts +182 -106
- package/src/lib/server/session-tools/web.ts +179 -349
- package/src/lib/server/storage.ts +24 -0
- package/src/lib/server/stream-agent-chat.ts +301 -244
- package/src/lib/server/task-quality-gate.test.ts +44 -0
- package/src/lib/server/task-quality-gate.ts +67 -0
- package/src/lib/server/task-validation.test.ts +78 -0
- package/src/lib/server/task-validation.ts +67 -2
- package/src/lib/server/tool-aliases.ts +68 -0
- package/src/lib/server/tool-capability-policy.ts +23 -5
- package/src/lib/tasks.ts +7 -1
- package/src/lib/tool-definitions.ts +23 -23
- package/src/lib/validation/schemas.ts +12 -0
- package/src/lib/view-routes.ts +2 -24
- package/src/stores/use-app-store.ts +23 -1
- package/src/types/index.ts +121 -7
|
@@ -34,6 +34,140 @@ export const MEMORY_FTS_STOP_WORDS = new Set([
|
|
|
34
34
|
'you', 'your',
|
|
35
35
|
])
|
|
36
36
|
|
|
37
|
+
export type MemoryScopeMode = 'auto' | 'all' | 'global' | 'agent' | 'session' | 'project'
|
|
38
|
+
export type MemoryRerankMode = 'balanced' | 'semantic' | 'lexical'
|
|
39
|
+
|
|
40
|
+
export interface MemoryScopeFilter {
|
|
41
|
+
mode: MemoryScopeMode
|
|
42
|
+
agentId?: string | null
|
|
43
|
+
sessionId?: string | null
|
|
44
|
+
projectRoot?: string | null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MemorySearchOptions {
|
|
48
|
+
scope?: MemoryScopeFilter
|
|
49
|
+
rerankMode?: MemoryRerankMode
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizeScopeIdentifier(value: unknown): string | null {
|
|
53
|
+
if (typeof value !== 'string') return null
|
|
54
|
+
const trimmed = value.trim()
|
|
55
|
+
return trimmed ? trimmed : null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizePathForScope(value: unknown): string | null {
|
|
59
|
+
if (typeof value !== 'string') return null
|
|
60
|
+
const trimmed = value.trim()
|
|
61
|
+
if (!trimmed) return null
|
|
62
|
+
return path.normalize(trimmed).replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function memorySearchText(entry: MemoryEntry): string {
|
|
66
|
+
const refs = Array.isArray(entry.references)
|
|
67
|
+
? entry.references.map((ref) => `${ref.type} ${ref.path || ''} ${ref.title || ''} ${ref.note || ''}`).join(' ')
|
|
68
|
+
: ''
|
|
69
|
+
return `${entry.title || ''} ${entry.content || ''} ${refs}`.toLowerCase()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function tokenizeForRerank(input: string): string[] {
|
|
73
|
+
const raw = String(input || '').toLowerCase().match(/[a-z0-9][a-z0-9._:/-]*/g) || []
|
|
74
|
+
const out: string[] = []
|
|
75
|
+
const seen = new Set<string>()
|
|
76
|
+
for (const token of raw) {
|
|
77
|
+
if (token.length < 2) continue
|
|
78
|
+
if (MEMORY_FTS_STOP_WORDS.has(token)) continue
|
|
79
|
+
if (seen.has(token)) continue
|
|
80
|
+
seen.add(token)
|
|
81
|
+
out.push(token)
|
|
82
|
+
}
|
|
83
|
+
return out
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function keywordOverlapScore(queryTokens: string[], entryText: string): number {
|
|
87
|
+
if (!queryTokens.length) return 0
|
|
88
|
+
const corpus = entryText.toLowerCase()
|
|
89
|
+
let matched = 0
|
|
90
|
+
for (const token of queryTokens) {
|
|
91
|
+
if (token.length < 3) continue
|
|
92
|
+
if (corpus.includes(token)) matched++
|
|
93
|
+
}
|
|
94
|
+
return matched / queryTokens.length
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function entryRootsForScope(entry: MemoryEntry): string[] {
|
|
98
|
+
const roots = new Set<string>()
|
|
99
|
+
const add = (raw: unknown) => {
|
|
100
|
+
const normalized = normalizePathForScope(raw)
|
|
101
|
+
if (normalized) roots.add(normalized)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
add((entry.metadata as Record<string, unknown> | undefined)?.projectRoot)
|
|
105
|
+
|
|
106
|
+
if (Array.isArray(entry.references)) {
|
|
107
|
+
for (const ref of entry.references) {
|
|
108
|
+
add(ref.projectRoot)
|
|
109
|
+
if (ref.type === 'project') add(ref.path)
|
|
110
|
+
if (ref.type === 'folder' || ref.type === 'file') add(ref.path)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (Array.isArray(entry.filePaths)) {
|
|
115
|
+
for (const ref of entry.filePaths) {
|
|
116
|
+
add(ref.projectRoot)
|
|
117
|
+
add(ref.path)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return [...roots]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function scopeAllowsAgentAccess(entry: MemoryEntry, agentId: string): boolean {
|
|
125
|
+
if (entry.agentId === agentId) return true
|
|
126
|
+
if (Array.isArray(entry.sharedWith) && entry.sharedWith.includes(agentId)) return true
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function normalizeMemoryScopeMode(raw: unknown): MemoryScopeMode {
|
|
131
|
+
const value = typeof raw === 'string' ? raw.trim().toLowerCase() : ''
|
|
132
|
+
if (value === 'shared') return 'global'
|
|
133
|
+
if (value === 'all' || value === 'global' || value === 'agent' || value === 'session' || value === 'project') return value
|
|
134
|
+
return 'auto'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function filterMemoriesByScope(entries: MemoryEntry[], scope?: MemoryScopeFilter): MemoryEntry[] {
|
|
138
|
+
if (!scope || scope.mode === 'all') return entries
|
|
139
|
+
const mode = normalizeMemoryScopeMode(scope.mode)
|
|
140
|
+
const agentId = normalizeScopeIdentifier(scope.agentId)
|
|
141
|
+
const sessionId = normalizeScopeIdentifier(scope.sessionId)
|
|
142
|
+
const projectRoot = normalizePathForScope(scope.projectRoot)
|
|
143
|
+
|
|
144
|
+
if (mode === 'global') {
|
|
145
|
+
return entries.filter((entry) => !entry.agentId)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (mode === 'agent') {
|
|
149
|
+
if (!agentId) return []
|
|
150
|
+
return entries.filter((entry) => scopeAllowsAgentAccess(entry, agentId))
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (mode === 'session') {
|
|
154
|
+
if (!sessionId) return []
|
|
155
|
+
return entries.filter((entry) => entry.sessionId === sessionId)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (mode === 'project') {
|
|
159
|
+
if (!projectRoot) return []
|
|
160
|
+
return entries.filter((entry) => {
|
|
161
|
+
const roots = entryRootsForScope(entry)
|
|
162
|
+
return roots.some((root) => root === projectRoot || root.startsWith(`${projectRoot}/`))
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// auto
|
|
167
|
+
if (!agentId) return entries
|
|
168
|
+
return entries.filter((entry) => !entry.agentId || scopeAllowsAgentAccess(entry, agentId))
|
|
169
|
+
}
|
|
170
|
+
|
|
37
171
|
function computeContentHash(category: string, content: string): string {
|
|
38
172
|
const normalized = `${category}|${content.toLowerCase().trim()}`
|
|
39
173
|
return createHash('sha256').update(normalized).digest('hex').slice(0, 16)
|
|
@@ -612,8 +746,8 @@ function initDb() {
|
|
|
612
746
|
const getAllWithEmbeddings = db.prepare(
|
|
613
747
|
`SELECT * FROM memories WHERE embedding IS NOT NULL`
|
|
614
748
|
)
|
|
615
|
-
const
|
|
616
|
-
`SELECT * FROM memories WHERE embedding IS NOT NULL AND agentId =
|
|
749
|
+
const getAllWithEmbeddingsByAgentOrShared = db.prepare(
|
|
750
|
+
`SELECT * FROM memories WHERE embedding IS NOT NULL AND (agentId = ? OR sharedWith LIKE ?)`
|
|
617
751
|
)
|
|
618
752
|
|
|
619
753
|
return {
|
|
@@ -852,17 +986,35 @@ function initDb() {
|
|
|
852
986
|
return this.get(id)
|
|
853
987
|
},
|
|
854
988
|
|
|
855
|
-
search(query: string, agentId?: string): MemoryEntry[] {
|
|
989
|
+
search(query: string, agentId?: string, options: MemorySearchOptions = {}): MemoryEntry[] {
|
|
856
990
|
if (shouldSkipSearchQuery(query)) return []
|
|
857
991
|
const startedAt = Date.now()
|
|
992
|
+
const normalizedAgentId = normalizeScopeIdentifier(agentId)
|
|
993
|
+
const rerankMode: MemoryRerankMode = options.rerankMode === 'semantic' || options.rerankMode === 'lexical'
|
|
994
|
+
? options.rerankMode
|
|
995
|
+
: 'balanced'
|
|
996
|
+
const scopeMode = options.scope
|
|
997
|
+
? normalizeMemoryScopeMode(options.scope.mode)
|
|
998
|
+
: (normalizedAgentId ? 'agent' : 'all')
|
|
999
|
+
const scopeFilter: MemoryScopeFilter | undefined = scopeMode === 'all'
|
|
1000
|
+
? undefined
|
|
1001
|
+
: {
|
|
1002
|
+
mode: scopeMode,
|
|
1003
|
+
agentId: options.scope?.agentId ?? normalizedAgentId,
|
|
1004
|
+
sessionId: options.scope?.sessionId,
|
|
1005
|
+
projectRoot: options.scope?.projectRoot,
|
|
1006
|
+
}
|
|
1007
|
+
|
|
858
1008
|
// FTS keyword search (includes memories shared with this agent)
|
|
859
1009
|
const ftsQuery = buildFtsQuery(query)
|
|
1010
|
+
const fastAgentOnlyScope = scopeMode === 'agent' && !!normalizedAgentId
|
|
860
1011
|
const ftsResults: MemoryEntry[] = ftsQuery
|
|
861
|
-
? (
|
|
862
|
-
? stmts.searchByAgentOrShared.all(ftsQuery,
|
|
1012
|
+
? (fastAgentOnlyScope
|
|
1013
|
+
? stmts.searchByAgentOrShared.all(ftsQuery, normalizedAgentId, `%"${normalizedAgentId}"%`) as any[]
|
|
863
1014
|
: stmts.search.all(ftsQuery) as any[]
|
|
864
1015
|
).map(rowToEntry)
|
|
865
1016
|
: []
|
|
1017
|
+
const ftsHitIds = new Set<string>(ftsResults.map((entry) => entry.id))
|
|
866
1018
|
|
|
867
1019
|
// Attempt vector search (synchronous — uses cached embedding if available)
|
|
868
1020
|
const vectorSimilarityScores = new Map<string, number>()
|
|
@@ -873,8 +1025,8 @@ function initDb() {
|
|
|
873
1025
|
const queryEmbedding = getEmbeddingSync(query)
|
|
874
1026
|
queryEmbeddingResult = queryEmbedding || undefined
|
|
875
1027
|
if (queryEmbedding) {
|
|
876
|
-
const rows =
|
|
877
|
-
?
|
|
1028
|
+
const rows = fastAgentOnlyScope
|
|
1029
|
+
? getAllWithEmbeddingsByAgentOrShared.all(normalizedAgentId, `%"${normalizedAgentId}"%`) as any[]
|
|
878
1030
|
: getAllWithEmbeddings.all() as any[]
|
|
879
1031
|
|
|
880
1032
|
const scored = rows
|
|
@@ -907,24 +1059,34 @@ function initDb() {
|
|
|
907
1059
|
merged.push(entry)
|
|
908
1060
|
}
|
|
909
1061
|
}
|
|
1062
|
+
const scopedMerged = scopeFilter ? filterMemoriesByScope(merged, scopeFilter) : merged
|
|
910
1063
|
|
|
911
|
-
//
|
|
1064
|
+
// Retrieval v2 rerank: hybrid relevance (semantic + lexical + FTS signal), then salience decay/boosting.
|
|
1065
|
+
const queryTokens = tokenizeForRerank(query)
|
|
912
1066
|
const now = Date.now()
|
|
913
1067
|
const HALF_LIFE_DAYS = 30
|
|
914
|
-
const salienceScored =
|
|
915
|
-
const
|
|
1068
|
+
const salienceScored = scopedMerged.map((entry) => {
|
|
1069
|
+
const semantic = vectorSimilarityScores.get(entry.id) ?? (ftsHitIds.has(entry.id) ? 0.42 : 0.18)
|
|
1070
|
+
const lexical = keywordOverlapScore(queryTokens, memorySearchText(entry))
|
|
1071
|
+
const ftsSignal = ftsHitIds.has(entry.id) ? 1 : 0
|
|
1072
|
+
const relevance = rerankMode === 'semantic'
|
|
1073
|
+
? (semantic * 0.78 + ftsSignal * 0.22)
|
|
1074
|
+
: rerankMode === 'lexical'
|
|
1075
|
+
? (lexical * 0.78 + ftsSignal * 0.22)
|
|
1076
|
+
: (semantic * 0.50 + lexical * 0.35 + ftsSignal * 0.15)
|
|
916
1077
|
const daysSinceAccess = (now - (entry.lastAccessedAt || entry.updatedAt)) / 86_400_000
|
|
917
1078
|
const recencyDecay = Math.exp(-0.693 * daysSinceAccess / HALF_LIFE_DAYS)
|
|
918
1079
|
const reinforcement = Math.log((entry.reinforcementCount || 0) + 1) + 1
|
|
919
1080
|
const pinnedBoost = entry.pinned ? 1.5 : 1.0
|
|
920
|
-
const salience =
|
|
1081
|
+
const salience = Math.max(0.0001, relevance) * recencyDecay * reinforcement * pinnedBoost
|
|
921
1082
|
return { entry, salience, embedding: rawEmbeddings.get(entry.id) }
|
|
922
1083
|
})
|
|
923
|
-
|
|
924
|
-
// Apply MMR
|
|
1084
|
+
|
|
1085
|
+
// Apply MMR when semantic reranking is enabled and query embeddings are available.
|
|
925
1086
|
let out: MemoryEntry[] = []
|
|
926
|
-
|
|
927
|
-
|
|
1087
|
+
const useMmr = !!queryEmbeddingResult && rerankMode !== 'lexical'
|
|
1088
|
+
if (useMmr && queryEmbeddingResult) {
|
|
1089
|
+
out = applyMMR(queryEmbeddingResult, salienceScored, MAX_MERGED_RESULTS, 0.6)
|
|
928
1090
|
} else {
|
|
929
1091
|
salienceScored.sort((a, b) => b.salience - a.salience)
|
|
930
1092
|
out = salienceScored.slice(0, MAX_MERGED_RESULTS).map((s) => s.entry)
|
|
@@ -944,7 +1106,7 @@ function initDb() {
|
|
|
944
1106
|
const elapsed = Date.now() - startedAt
|
|
945
1107
|
if (elapsed > 1200) {
|
|
946
1108
|
console.warn(
|
|
947
|
-
`[memory-db] Slow search ${elapsed}ms (
|
|
1109
|
+
`[memory-db] Slow search ${elapsed}ms (scope=${scopeMode}, rerank=${rerankMode}, rawLen=${String(query || '').length}, fts="${ftsQuery.slice(0, 180)}")`,
|
|
948
1110
|
)
|
|
949
1111
|
}
|
|
950
1112
|
return out
|
|
@@ -957,8 +1119,9 @@ function initDb() {
|
|
|
957
1119
|
maxDepth?: number,
|
|
958
1120
|
maxResults?: number,
|
|
959
1121
|
maxLinkedExpansion?: number,
|
|
1122
|
+
options: MemorySearchOptions = {},
|
|
960
1123
|
): { entries: MemoryEntry[]; truncated: boolean; expandedLinkedCount: number; limits: MemoryLookupLimits } {
|
|
961
|
-
const baseResults = this.search(query, agentId)
|
|
1124
|
+
const baseResults = this.search(query, agentId, options)
|
|
962
1125
|
const defaults = getMemoryLookupLimits()
|
|
963
1126
|
const limits = resolveLookupRequest(defaults, {
|
|
964
1127
|
depth: maxDepth ?? defaults.maxDepth,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import type { MemoryEntry } from '@/types'
|
|
4
|
+
import { filterMemoriesByScope, normalizeMemoryScopeMode } from './memory-db.ts'
|
|
5
|
+
|
|
6
|
+
function makeEntry(id: string, patch: Partial<MemoryEntry> = {}): MemoryEntry {
|
|
7
|
+
return {
|
|
8
|
+
id,
|
|
9
|
+
agentId: null,
|
|
10
|
+
sessionId: null,
|
|
11
|
+
category: 'note',
|
|
12
|
+
title: `Entry ${id}`,
|
|
13
|
+
content: `content ${id}`,
|
|
14
|
+
createdAt: 1,
|
|
15
|
+
updatedAt: 1,
|
|
16
|
+
...patch,
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test('normalizeMemoryScopeMode maps shared alias to global', () => {
|
|
21
|
+
assert.equal(normalizeMemoryScopeMode('shared'), 'global')
|
|
22
|
+
assert.equal(normalizeMemoryScopeMode('project'), 'project')
|
|
23
|
+
assert.equal(normalizeMemoryScopeMode('unknown'), 'auto')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('filterMemoriesByScope auto includes global + own + shared-with-agent', () => {
|
|
27
|
+
const rows: MemoryEntry[] = [
|
|
28
|
+
makeEntry('global', { agentId: null }),
|
|
29
|
+
makeEntry('mine', { agentId: 'agent-a' }),
|
|
30
|
+
makeEntry('shared', { agentId: 'agent-b', sharedWith: ['agent-a'] }),
|
|
31
|
+
makeEntry('other', { agentId: 'agent-b' }),
|
|
32
|
+
]
|
|
33
|
+
const filtered = filterMemoriesByScope(rows, { mode: 'auto', agentId: 'agent-a' })
|
|
34
|
+
assert.deepEqual(filtered.map((r) => r.id), ['global', 'mine', 'shared'])
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('filterMemoriesByScope supports project and session scopes', () => {
|
|
38
|
+
const rows: MemoryEntry[] = [
|
|
39
|
+
makeEntry('proj-hit', {
|
|
40
|
+
sessionId: 's-1',
|
|
41
|
+
references: [{ type: 'project', path: '/repo/swarm', projectRoot: '/repo/swarm', timestamp: 1 }],
|
|
42
|
+
}),
|
|
43
|
+
makeEntry('proj-miss', {
|
|
44
|
+
sessionId: 's-1',
|
|
45
|
+
references: [{ type: 'project', path: '/repo/other', projectRoot: '/repo/other', timestamp: 1 }],
|
|
46
|
+
}),
|
|
47
|
+
makeEntry('session-hit', { sessionId: 's-2' }),
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const projectFiltered = filterMemoriesByScope(rows, { mode: 'project', projectRoot: '/repo/swarm' })
|
|
51
|
+
assert.deepEqual(projectFiltered.map((r) => r.id), ['proj-hit'])
|
|
52
|
+
|
|
53
|
+
const sessionFiltered = filterMemoriesByScope(rows, { mode: 'session', sessionId: 's-2' })
|
|
54
|
+
assert.deepEqual(sessionFiltered.map((r) => r.id), ['session-hit'])
|
|
55
|
+
})
|
|
56
|
+
|
|
@@ -604,7 +604,10 @@ export async function executeLangGraphOrchestrator(
|
|
|
604
604
|
|
|
605
605
|
// Extract summary from mark_complete if present
|
|
606
606
|
const completeMatch = finalResult.match(/ORCHESTRATION_COMPLETE:\s*([\s\S]+)/)
|
|
607
|
-
|
|
607
|
+
const summary = completeMatch ? completeMatch[1].trim() : finalResult
|
|
608
|
+
|
|
609
|
+
// Append meta so the main-loop knows we are done
|
|
610
|
+
return `${summary}\n\n[MAIN_LOOP_META] {"status":"ok", "follow_up":false, "summary":${JSON.stringify(summary.slice(0, 300))}, "mission_task_id":${JSON.stringify(taskId || null)}}`
|
|
608
611
|
}
|
|
609
612
|
|
|
610
613
|
/**
|
|
@@ -77,13 +77,14 @@ export async function executeOrchestrator(
|
|
|
77
77
|
|
|
78
78
|
// claude-cli fallback (no structured tool calling)
|
|
79
79
|
console.warn(`[orchestrator] Using legacy regex-based engine for ${orchestrator.name} (claude-cli)`)
|
|
80
|
-
return executeOrchestratorLegacy(orchestrator, task, sessionId)
|
|
80
|
+
return executeOrchestratorLegacy(orchestrator, task, sessionId, taskId)
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
async function executeOrchestratorLegacy(
|
|
84
84
|
orchestrator: Agent,
|
|
85
85
|
task: string,
|
|
86
86
|
sessionId: string,
|
|
87
|
+
taskId?: string,
|
|
87
88
|
): Promise<string> {
|
|
88
89
|
const allAgents = loadAgents()
|
|
89
90
|
const sessions = loadSessions()
|
|
@@ -240,7 +241,7 @@ async function executeOrchestratorLegacy(
|
|
|
240
241
|
|
|
241
242
|
if (cmd.done) {
|
|
242
243
|
result = cmd.summary || fullText
|
|
243
|
-
return result
|
|
244
|
+
return `${result}\n\n[MAIN_LOOP_META] {"status":"ok", "follow_up":false, "summary":${JSON.stringify(result.slice(0, 300))}, "mission_task_id":${JSON.stringify(taskId || null)}}`
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
247
|
|
|
@@ -255,7 +256,7 @@ async function executeOrchestratorLegacy(
|
|
|
255
256
|
result = `Loop stopped after reaching max turns (${maxTurns}).`
|
|
256
257
|
}
|
|
257
258
|
|
|
258
|
-
return result
|
|
259
|
+
return `${result}\n\n[MAIN_LOOP_META] {"status":"ok", "follow_up":false, "summary":${JSON.stringify(result.slice(0, 300))}, "mission_task_id":${JSON.stringify(taskId || null)}}`
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
async function executeSubTask(
|