crewswarm 0.9.4 → 1.0.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/.env.example +8 -1
- package/README.md +58 -9
- package/apps/dashboard/README.md +49 -0
- package/apps/dashboard/dist/assets/{index-D-sRshvg.css → index-C5-vlIwl.css} +1 -1
- package/apps/dashboard/dist/assets/index-CSooN9fi.js +2 -0
- package/apps/dashboard/dist/assets/index-CSooN9fi.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-DcXD5TQY.js +1 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-DcXD5TQY.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-Ea5K-rsb.js +1 -0
- package/apps/dashboard/dist/index.html +85 -7
- package/apps/dashboard/dist/index.html.br +0 -0
- package/contrib/openclaw-plugin/index.ts +20 -11
- package/install.sh +2 -2
- package/lib/autoharness/index.mjs +151 -1
- package/lib/chat/history.mjs +1 -1
- package/lib/contacts/identity-linker.mjs +24 -3
- package/lib/contacts/index.mjs +2 -1
- package/lib/crew-lead/chat-handler.mjs +56 -33
- package/lib/crew-lead/llm-caller.mjs +71 -14
- package/lib/crew-lead/prompts.mjs +4 -2
- package/lib/crew-lead/wave-dispatcher.mjs +53 -3
- package/lib/crew-lead/worktree.mjs +258 -0
- package/lib/crew-lead/ws-router.mjs +43 -0
- package/lib/engines/rt-envelope.mjs +4 -1
- package/lib/memory/relevance-scorer.mjs +199 -0
- package/lib/memory/shared-adapter.mjs +85 -19
- package/package.json +10 -3
- package/scripts/dashboard.mjs +398 -28
- package/scripts/health-check.mjs +70 -28
- package/scripts/install-docker.sh +1 -1
- package/scripts/restart-all-from-repo.sh +25 -21
- package/scripts/start.mjs +81 -26
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
- package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
- package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js +0 -2
- package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
- package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
- package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
- package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js +0 -1
- package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +0 -1
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
|
@@ -1068,11 +1068,14 @@ export async function handleRealtimeEnvelope(envelope, client, bridge) {
|
|
|
1068
1068
|
/and nothing else\b/i.test(prompt || "");
|
|
1069
1069
|
|
|
1070
1070
|
// Append original task spec for self-verification (LangChain pattern)
|
|
1071
|
-
// Skip strict-output prompts
|
|
1071
|
+
// Skip strict-output prompts and trivial/empty replies where the reminder adds noise.
|
|
1072
|
+
const replyStripped = (reply || "").replace(/\s+/g, " ").trim();
|
|
1073
|
+
const isTrivialReply = replyStripped.length < 50 || /^\(completed\)$/i.test(replyStripped);
|
|
1072
1074
|
if (
|
|
1073
1075
|
reply &&
|
|
1074
1076
|
prompt &&
|
|
1075
1077
|
!requestsExactReply &&
|
|
1078
|
+
!isTrivialReply &&
|
|
1076
1079
|
!reply.includes("[ORIGINAL TASK]")
|
|
1077
1080
|
) {
|
|
1078
1081
|
const taskSpecReminder = `\n\n---\n**[ORIGINAL TASK]:**\n${prompt.slice(0, 500)}${prompt.length > 500 ? "..." : ""}\n\nDoes your implementation address ALL requirements above?`;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Relevance Scorer
|
|
3
|
+
*
|
|
4
|
+
* Score memories by relevance to a query using:
|
|
5
|
+
* 1. Recency — newer memories score higher (exponential decay, ~30-day half-life)
|
|
6
|
+
* 2. Frequency — memories accessed more often score higher (normalised log)
|
|
7
|
+
* 3. Keyword — TF-IDF-like scoring against query terms (inverse-length weighting)
|
|
8
|
+
* 4. Context — memories from the same project/agent/session score higher
|
|
9
|
+
*
|
|
10
|
+
* Pure functions only — zero I/O, zero dependencies.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ─── Internal helpers ────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Tokenise a string into lowercase alpha-numeric tokens of length >= 2.
|
|
17
|
+
* @param {string} text
|
|
18
|
+
* @returns {string[]}
|
|
19
|
+
*/
|
|
20
|
+
function tokenise(text) {
|
|
21
|
+
if (!text || typeof text !== 'string') return [];
|
|
22
|
+
return text
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.replace(/[^a-z0-9\s_-]/g, ' ')
|
|
25
|
+
.split(/\s+/)
|
|
26
|
+
.filter(t => t.length >= 2);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── Individual scoring components ──────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Recency score: exponential decay with ~30-day half-life.
|
|
33
|
+
* Returns 1.0 for brand-new memories, approaching 0 for very old ones.
|
|
34
|
+
*
|
|
35
|
+
* @param {string|number|Date} timestamp - ISO string, epoch ms, or Date
|
|
36
|
+
* @param {number} nowMs - current epoch ms (injectable for testing)
|
|
37
|
+
* @returns {number} [0, 1]
|
|
38
|
+
*/
|
|
39
|
+
export function computeRecency(timestamp, nowMs = Date.now()) {
|
|
40
|
+
if (timestamp == null) return 0;
|
|
41
|
+
const createdAt = timestamp instanceof Date
|
|
42
|
+
? timestamp.getTime()
|
|
43
|
+
: new Date(timestamp).getTime();
|
|
44
|
+
if (Number.isNaN(createdAt)) return 0;
|
|
45
|
+
const daysSince = Math.max(0, (nowMs - createdAt) / (1000 * 60 * 60 * 24));
|
|
46
|
+
return Math.exp(-daysSince / 30);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Frequency score: log-normalised access count relative to a max.
|
|
51
|
+
* Both accessCount and maxAccessCount must be >= 0.
|
|
52
|
+
*
|
|
53
|
+
* @param {number} accessCount
|
|
54
|
+
* @param {number} maxAccessCount - upper bound for normalisation (default 100)
|
|
55
|
+
* @returns {number} [0, 1]
|
|
56
|
+
*/
|
|
57
|
+
export function computeFrequency(accessCount, maxAccessCount = 100) {
|
|
58
|
+
const count = Math.max(0, Number(accessCount) || 0);
|
|
59
|
+
const maxCount = Math.max(1, Number(maxAccessCount) || 100);
|
|
60
|
+
return Math.min(1, Math.log(1 + count) / Math.log(1 + maxCount));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Keyword match score: TF-IDF-like overlap between query tokens and memory content.
|
|
65
|
+
* Rarer (longer) query words are weighted more heavily.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} content - memory content
|
|
68
|
+
* @param {string} query
|
|
69
|
+
* @returns {number} [0, 1]
|
|
70
|
+
*/
|
|
71
|
+
export function computeKeywordMatch(content, query) {
|
|
72
|
+
const queryTokens = tokenise(query);
|
|
73
|
+
const contentTokens = tokenise(content);
|
|
74
|
+
|
|
75
|
+
if (queryTokens.length === 0 || contentTokens.length === 0) return 0;
|
|
76
|
+
|
|
77
|
+
const contentSet = new Set(contentTokens);
|
|
78
|
+
|
|
79
|
+
// Weight each query token by its length (longer words are more specific)
|
|
80
|
+
let weightedMatch = 0;
|
|
81
|
+
let totalWeight = 0;
|
|
82
|
+
|
|
83
|
+
for (const token of queryTokens) {
|
|
84
|
+
// IDF proxy: weight proportional to token length (longer = rarer heuristic)
|
|
85
|
+
const weight = Math.log(1 + token.length);
|
|
86
|
+
totalWeight += weight;
|
|
87
|
+
if (contentSet.has(token)) {
|
|
88
|
+
weightedMatch += weight;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (totalWeight === 0) return 0;
|
|
93
|
+
return weightedMatch / totalWeight;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Context match score: bonus points for shared project / agent / session.
|
|
98
|
+
*
|
|
99
|
+
* @param {object} memory - memory object with optional projectId, agentId, sessionId
|
|
100
|
+
* @param {object} context - { projectId?, agentId?, sessionId? }
|
|
101
|
+
* @returns {number} [0, 1]
|
|
102
|
+
*/
|
|
103
|
+
export function computeContextMatch(memory, context = {}) {
|
|
104
|
+
if (!memory || !context) return 0;
|
|
105
|
+
|
|
106
|
+
let score = 0;
|
|
107
|
+
|
|
108
|
+
if (context.projectId && memory.projectId &&
|
|
109
|
+
context.projectId === memory.projectId) {
|
|
110
|
+
score += 0.5;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (context.agentId && memory.agentId &&
|
|
114
|
+
context.agentId === memory.agentId) {
|
|
115
|
+
score += 0.3;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (context.sessionId && memory.sessionId &&
|
|
119
|
+
context.sessionId === memory.sessionId) {
|
|
120
|
+
score += 0.2;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Cap at 1.0
|
|
124
|
+
return Math.min(1, score);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Score a single memory object for relevance to a query + context.
|
|
131
|
+
*
|
|
132
|
+
* Expected memory shape (all fields optional except content):
|
|
133
|
+
* {
|
|
134
|
+
* content: string,
|
|
135
|
+
* timestamp: string|number|Date, // ISO or epoch ms
|
|
136
|
+
* accessCount: number,
|
|
137
|
+
* projectId: string,
|
|
138
|
+
* agentId: string,
|
|
139
|
+
* sessionId: string,
|
|
140
|
+
* }
|
|
141
|
+
*
|
|
142
|
+
* @param {object} memory
|
|
143
|
+
* @param {string} query
|
|
144
|
+
* @param {object} [context] - { projectId?, agentId?, sessionId? }
|
|
145
|
+
* @param {object} [opts]
|
|
146
|
+
* @param {number} [opts.nowMs] - override current time (for testing)
|
|
147
|
+
* @param {number} [opts.maxAccessCount] - normalisation ceiling for frequency
|
|
148
|
+
* @returns {number} weighted relevance score in [0, 1]
|
|
149
|
+
*/
|
|
150
|
+
export function scoreMemory(memory, query, context = {}, opts = {}) {
|
|
151
|
+
if (!memory) return 0;
|
|
152
|
+
|
|
153
|
+
const nowMs = opts.nowMs != null ? opts.nowMs : Date.now();
|
|
154
|
+
const maxAccessCount = opts.maxAccessCount != null ? opts.maxAccessCount : 100;
|
|
155
|
+
|
|
156
|
+
const recencyScore = computeRecency(memory.timestamp, nowMs);
|
|
157
|
+
const frequencyScore = computeFrequency(memory.accessCount || 0, maxAccessCount);
|
|
158
|
+
const keywordScore = computeKeywordMatch(memory.content || '', query);
|
|
159
|
+
const contextScore = computeContextMatch(memory, context);
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
0.30 * recencyScore +
|
|
163
|
+
0.20 * frequencyScore +
|
|
164
|
+
0.35 * keywordScore +
|
|
165
|
+
0.15 * contextScore
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Rank an array of memories by relevance and return the top N.
|
|
171
|
+
* Attaches a `relevanceScore` property to each returned object.
|
|
172
|
+
*
|
|
173
|
+
* @param {object[]} memories
|
|
174
|
+
* @param {string} query
|
|
175
|
+
* @param {object} [context] - { projectId?, agentId?, sessionId? }
|
|
176
|
+
* @param {number} [maxResults=10]
|
|
177
|
+
* @param {object} [opts] - forwarded to scoreMemory
|
|
178
|
+
* @returns {object[]} sorted slice with relevanceScore attached
|
|
179
|
+
*/
|
|
180
|
+
export function rankMemories(memories, query, context = {}, maxResults = 10, opts = {}) {
|
|
181
|
+
if (!Array.isArray(memories) || memories.length === 0) return [];
|
|
182
|
+
|
|
183
|
+
return memories
|
|
184
|
+
.map(m => ({ ...m, relevanceScore: scoreMemory(m, query, context, opts) }))
|
|
185
|
+
.sort((a, b) => b.relevanceScore - a.relevanceScore)
|
|
186
|
+
.slice(0, Math.max(1, maxResults));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Derive the max accessCount from a collection of memories.
|
|
191
|
+
* Useful for caller-side normalisation when passing opts.maxAccessCount.
|
|
192
|
+
*
|
|
193
|
+
* @param {object[]} memories
|
|
194
|
+
* @returns {number}
|
|
195
|
+
*/
|
|
196
|
+
export function maxAccessCount(memories) {
|
|
197
|
+
if (!Array.isArray(memories) || memories.length === 0) return 0;
|
|
198
|
+
return memories.reduce((max, m) => Math.max(max, m.accessCount || 0), 0);
|
|
199
|
+
}
|
|
@@ -12,6 +12,7 @@ import fs from 'node:fs';
|
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import os from 'node:os';
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
|
+
import { rankMemories, maxAccessCount } from './relevance-scorer.mjs';
|
|
15
16
|
|
|
16
17
|
const require = createRequire(import.meta.url);
|
|
17
18
|
|
|
@@ -124,41 +125,106 @@ export function rememberFact(agentId, content, options = {}) {
|
|
|
124
125
|
|
|
125
126
|
/**
|
|
126
127
|
* Recall memory context for a task using MemoryBroker (blends AgentKeeper + AgentMemory + Collections).
|
|
128
|
+
* ENHANCED: Applies relevance scoring (recency + frequency + keyword + context) and tracks access metadata.
|
|
127
129
|
* ENHANCED: Also includes relevant conversation history from project messages.
|
|
128
130
|
* @param {string} projectDir - Project directory
|
|
129
131
|
* @param {string} query - Task description or search query
|
|
130
|
-
* @param {object} options - { maxResults?, includeDocs?, includeCode?, pathHints?, preferSuccessful?, userId?, projectId? }
|
|
132
|
+
* @param {object} options - { maxResults?, includeDocs?, includeCode?, pathHints?, preferSuccessful?, userId?, projectId?, agentId?, sessionId? }
|
|
131
133
|
* @returns {Promise<string>} - Formatted context block
|
|
132
134
|
*/
|
|
133
135
|
export async function recallMemoryContext(projectDir, query, options = {}) {
|
|
134
136
|
const broker = getMemoryBroker(projectDir, { crewId: options.crewId || 'crew-lead' });
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
let memoryContext = '';
|
|
137
|
-
|
|
139
|
+
|
|
138
140
|
// Get standard memory (AgentKeeper + AgentMemory + Collections)
|
|
139
|
-
//
|
|
141
|
+
// Fetch a larger candidate set so the relevance ranker has room to reorder
|
|
140
142
|
if (broker) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
const maxResults = options.maxResults || 5;
|
|
144
|
+
const candidateLimit = Math.max(maxResults * 3, 15);
|
|
145
|
+
|
|
146
|
+
// Pull raw structured hits when available, fall back to formatted context
|
|
147
|
+
let rawHits = null;
|
|
148
|
+
if (typeof broker.recall === 'function') {
|
|
149
|
+
try {
|
|
150
|
+
rawHits = await broker.recall(query, {
|
|
151
|
+
maxResults: candidateLimit,
|
|
152
|
+
includeDocs: options.includeDocs !== false,
|
|
153
|
+
includeCode: Boolean(options.includeCode),
|
|
154
|
+
pathHints: options.pathHints || [],
|
|
155
|
+
preferSuccessful: options.preferSuccessful !== false,
|
|
156
|
+
minScore: 0.7,
|
|
157
|
+
excludeFailed: true,
|
|
158
|
+
excludeErrors: true,
|
|
159
|
+
excludeTimeouts: true
|
|
160
|
+
});
|
|
161
|
+
} catch {
|
|
162
|
+
rawHits = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (rawHits && Array.isArray(rawHits) && rawHits.length > 0) {
|
|
167
|
+
// Build a relevance context for the ranker
|
|
168
|
+
const scoringContext = {
|
|
169
|
+
projectId: options.projectId,
|
|
170
|
+
agentId: options.agentId,
|
|
171
|
+
sessionId: options.sessionId
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Normalise hits to the shape scoreMemory expects
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
const normalised = rawHits.map(hit => ({
|
|
177
|
+
...hit,
|
|
178
|
+
content: hit.text || hit.content || '',
|
|
179
|
+
timestamp: hit.metadata?.timestamp || hit.timestamp || new Date(now - 86400000).toISOString(),
|
|
180
|
+
accessCount: (hit.accessCount || 0) + 1, // count this retrieval
|
|
181
|
+
lastAccessed: new Date(now).toISOString(),
|
|
182
|
+
projectId: hit.metadata?.projectId || hit.projectId,
|
|
183
|
+
agentId: hit.metadata?.agentId || hit.agentId,
|
|
184
|
+
sessionId: hit.metadata?.sessionId || hit.sessionId
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
const scoringOpts = { nowMs: now, maxAccessCount: maxAccessCount(normalised) };
|
|
188
|
+
const ranked = rankMemories(normalised, query, scoringContext, maxResults, scoringOpts);
|
|
189
|
+
|
|
190
|
+
// Re-serialise to context string (mirrors broker.recallAsContext format)
|
|
191
|
+
if (ranked.length > 0) {
|
|
192
|
+
const lines = ranked.map((hit, i) => {
|
|
193
|
+
const score = hit.relevanceScore.toFixed(3);
|
|
194
|
+
const source = hit.source || hit.metadata?.source || 'memory';
|
|
195
|
+
const title = hit.title || hit.metadata?.title || `Result ${i + 1}`;
|
|
196
|
+
return `[${source}] ${title} (relevance: ${score})\n${hit.content}`;
|
|
197
|
+
});
|
|
198
|
+
memoryContext = lines.join('\n\n---\n\n');
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
// Fallback: broker doesn't expose raw hits — use formatted context as-is
|
|
202
|
+
try {
|
|
203
|
+
memoryContext = await broker.recallAsContext(query, {
|
|
204
|
+
maxResults: options.maxResults || 5,
|
|
205
|
+
includeDocs: options.includeDocs !== false,
|
|
206
|
+
includeCode: Boolean(options.includeCode),
|
|
207
|
+
pathHints: options.pathHints || [],
|
|
208
|
+
preferSuccessful: options.preferSuccessful !== false,
|
|
209
|
+
// Quality filters to prevent context contamination
|
|
210
|
+
minScore: 0.7,
|
|
211
|
+
excludeFailed: true,
|
|
212
|
+
excludeErrors: true,
|
|
213
|
+
excludeTimeouts: true
|
|
214
|
+
});
|
|
215
|
+
} catch {
|
|
216
|
+
memoryContext = '';
|
|
217
|
+
}
|
|
218
|
+
}
|
|
153
219
|
}
|
|
154
|
-
|
|
220
|
+
|
|
155
221
|
// ENHANCEMENT: Add relevant conversation history from project messages
|
|
156
222
|
// This lets agents see past discussions about the same topic
|
|
157
223
|
if (options.projectId) {
|
|
158
224
|
try {
|
|
159
225
|
const ragModule = await getProjectMessagesRag();
|
|
160
226
|
const conversationContext = ragModule?.getConversationContext(options.projectId, query, 3);
|
|
161
|
-
|
|
227
|
+
|
|
162
228
|
if (conversationContext) {
|
|
163
229
|
memoryContext += (memoryContext ? '\n\n' : '') + conversationContext;
|
|
164
230
|
}
|
|
@@ -167,7 +233,7 @@ export async function recallMemoryContext(projectDir, query, options = {}) {
|
|
|
167
233
|
console.warn('[shared-adapter] Failed to load conversation context:', e.message);
|
|
168
234
|
}
|
|
169
235
|
}
|
|
170
|
-
|
|
236
|
+
|
|
171
237
|
return memoryContext;
|
|
172
238
|
}
|
|
173
239
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewswarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Local-first multi-agent orchestration platform — coordinate AI coding agents, LLMs, and tools from a single dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/crewswarm/crewswarm.git"
|
|
10
10
|
},
|
|
11
|
+
"homepage": "https://crewswarm.ai",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/crewswarm/crewswarm/issues"
|
|
14
|
+
},
|
|
11
15
|
"keywords": [
|
|
12
16
|
"ai",
|
|
13
17
|
"agents",
|
|
@@ -110,6 +114,7 @@
|
|
|
110
114
|
"vibe:start": "cd apps/vibe && NODE_DISABLE_COMPILE_CACHE=1 npm start",
|
|
111
115
|
"vibe:watch": "NODE_DISABLE_COMPILE_CACHE=1 node apps/vibe/watch-server.mjs",
|
|
112
116
|
"vibe:full": "bash scripts/start-studio-full.sh",
|
|
117
|
+
"test:playwright": "npx playwright test --reporter=line",
|
|
113
118
|
"test:e2e:vibe": "node node_modules/playwright/cli.js test --config=playwright.config.js",
|
|
114
119
|
"test:e2e:vibe:headed": "node node_modules/playwright/cli.js test --config=playwright.config.js --headed",
|
|
115
120
|
"crew-lead": "node crew-lead.mjs",
|
|
@@ -128,10 +133,12 @@
|
|
|
128
133
|
"release:check": "bash scripts/release-check.sh",
|
|
129
134
|
"test:report": "node scripts/test-report-summary.mjs",
|
|
130
135
|
"test:rerun": "node scripts/test-rerun.mjs",
|
|
131
|
-
"test:stale": "node scripts/test-rerun.mjs --stale"
|
|
136
|
+
"test:stale": "node scripts/test-rerun.mjs --stale",
|
|
137
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
132
138
|
},
|
|
133
139
|
"devDependencies": {
|
|
134
140
|
"@playwright/test": "^1.58.2",
|
|
135
|
-
"puppeteer-core": "^24.40.0"
|
|
141
|
+
"puppeteer-core": "^24.40.0",
|
|
142
|
+
"typescript": "^5.9.3"
|
|
136
143
|
}
|
|
137
144
|
}
|