claudeck 1.3.1 → 1.4.1
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 +13 -9
- package/db/sqlite.js +1697 -0
- package/db.js +3 -1645
- package/package.json +2 -1
- package/plugins/claude-editor/manifest.json +10 -0
- package/plugins/linear/manifest.json +10 -0
- package/plugins/repos/manifest.json +10 -0
- package/public/css/ui/messages.css +25 -0
- package/public/css/ui/right-panel.css +207 -0
- package/public/css/ui/settings.css +75 -0
- package/public/index.html +7 -0
- package/public/js/components/settings-modal.js +65 -0
- package/public/js/core/api.js +23 -6
- package/public/js/core/events.js +11 -0
- package/public/js/core/plugin-loader.js +96 -11
- package/public/js/core/store.js +11 -0
- package/public/js/core/ws.js +12 -0
- package/public/js/features/chat.js +4 -0
- package/public/js/features/sessions.js +102 -10
- package/public/js/main.js +1 -0
- package/public/js/panels/assistant-bot.js +16 -0
- package/public/js/panels/dev-docs.js +2 -2
- package/public/js/panels/memory.js +1 -0
- package/public/js/ui/context-gauge.js +10 -1
- package/public/js/ui/header-dropdowns.js +30 -0
- package/public/js/ui/input-meta.js +13 -6
- package/public/js/ui/max-turns.js +6 -3
- package/public/js/ui/messages.js +42 -0
- package/public/js/ui/model-selector.js +1 -0
- package/public/js/ui/parallel.js +2 -4
- package/public/js/ui/permissions.js +1 -0
- package/public/js/ui/tab-sdk.js +395 -176
- package/public/style.css +1 -0
- package/server/agent-loop.js +26 -26
- package/server/memory-extractor.js +4 -4
- package/server/memory-injector.js +11 -11
- package/server/memory-optimizer.js +19 -15
- package/server/notification-logger.js +5 -5
- package/server/orchestrator.js +15 -15
- package/server/push-sender.js +2 -2
- package/server/routes/agents.js +2 -2
- package/server/routes/marketplace.js +316 -0
- package/server/routes/memory.js +20 -20
- package/server/routes/messages.js +41 -10
- package/server/routes/notifications.js +20 -20
- package/server/routes/sessions.js +17 -17
- package/server/routes/stats.js +37 -37
- package/server/routes/worktrees.js +9 -9
- package/server/summarizer.js +3 -3
- package/server/ws-handler.js +163 -58
- package/server.js +20 -2
- package/plugins/event-stream/client.css +0 -207
- package/plugins/event-stream/client.js +0 -271
- package/plugins/sudoku/client.css +0 -196
- package/plugins/sudoku/client.js +0 -329
- package/plugins/tasks/client.css +0 -414
- package/plugins/tasks/client.js +0 -394
- package/plugins/tasks/server.js +0 -116
- package/plugins/tic-tac-toe/client.css +0 -167
- package/plugins/tic-tac-toe/client.js +0 -241
package/public/style.css
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
@import url("css/panels/git-panel.css");
|
|
25
25
|
|
|
26
26
|
@import url("css/panels/mcp-manager.css");
|
|
27
|
+
@import url("css/ui/settings.css");
|
|
27
28
|
@import url("css/ui/image-attachments.css");
|
|
28
29
|
@import url("css/panels/tips-feed.css");
|
|
29
30
|
@import url("css/ui/context-gauge.css");
|
package/server/agent-loop.js
CHANGED
|
@@ -40,7 +40,7 @@ import { logNotification } from "./notification-logger.js";
|
|
|
40
40
|
* Build the agent system prompt that instructs Claude to work autonomously
|
|
41
41
|
* toward the given goal.
|
|
42
42
|
*/
|
|
43
|
-
function buildAgentPrompt(agentDef, userContext, sharedContext, cwd) {
|
|
43
|
+
async function buildAgentPrompt(agentDef, userContext, sharedContext, cwd) {
|
|
44
44
|
let prompt = `You are an autonomous AI agent. Work toward the following goal step by step, using any tools available to you.\n\n`;
|
|
45
45
|
prompt += `## Goal\n${agentDef.goal}\n\n`;
|
|
46
46
|
if (userContext) {
|
|
@@ -48,7 +48,7 @@ function buildAgentPrompt(agentDef, userContext, sharedContext, cwd) {
|
|
|
48
48
|
}
|
|
49
49
|
// Inject persistent memories from previous sessions
|
|
50
50
|
if (cwd) {
|
|
51
|
-
const memoryPrompt = buildAgentMemoryPrompt(cwd, 8);
|
|
51
|
+
const memoryPrompt = await buildAgentMemoryPrompt(cwd, 8);
|
|
52
52
|
if (memoryPrompt) {
|
|
53
53
|
prompt += memoryPrompt + '\n\n';
|
|
54
54
|
console.log(`\n══════ AGENT MEMORY INJECTION ══════`);
|
|
@@ -105,7 +105,7 @@ export async function runAgent({
|
|
|
105
105
|
const monitorRunId = runId || `single-${Date.now()}`;
|
|
106
106
|
const effectiveRunType = runType || 'single';
|
|
107
107
|
try {
|
|
108
|
-
recordAgentRunStart(monitorRunId, agentId, agentDef.title, effectiveRunType, parentRunId);
|
|
108
|
+
await recordAgentRunStart(monitorRunId, agentId, agentDef.title, effectiveRunType, parentRunId);
|
|
109
109
|
} catch (e) { /* ignore duplicates */ }
|
|
110
110
|
|
|
111
111
|
function agentSend(payload) {
|
|
@@ -161,8 +161,8 @@ export async function runAgent({
|
|
|
161
161
|
if (resumeId) opts.resume = resumeId;
|
|
162
162
|
|
|
163
163
|
// Load shared context from previous agents in this run
|
|
164
|
-
const sharedContext = runId ? getAllAgentContext(runId) : [];
|
|
165
|
-
const prompt = buildAgentPrompt(agentDef, userContext, sharedContext, cwd);
|
|
164
|
+
const sharedContext = runId ? await getAllAgentContext(runId) : [];
|
|
165
|
+
const prompt = await buildAgentPrompt(agentDef, userContext, sharedContext, cwd);
|
|
166
166
|
let resolvedSid = clientSid;
|
|
167
167
|
let claudeSessionId = null;
|
|
168
168
|
let sessionModel = null;
|
|
@@ -185,15 +185,15 @@ export async function runAgent({
|
|
|
185
185
|
|
|
186
186
|
sessionIds.set(ourSid, claudeSessionId);
|
|
187
187
|
|
|
188
|
-
if (!getSession(ourSid)) {
|
|
189
|
-
createSession(ourSid, claudeSessionId, projectName || "Agent Session", cwd || "");
|
|
190
|
-
updateSessionTitle(ourSid, `Agent: ${agentDef.title}`);
|
|
188
|
+
if (!await getSession(ourSid)) {
|
|
189
|
+
await createSession(ourSid, claudeSessionId, projectName || "Agent Session", cwd || "");
|
|
190
|
+
await updateSessionTitle(ourSid, `Agent: ${agentDef.title}`);
|
|
191
191
|
} else {
|
|
192
|
-
updateClaudeSessionId(ourSid, claudeSessionId);
|
|
192
|
+
await updateClaudeSessionId(ourSid, claudeSessionId);
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
agentSend({ type: "session", sessionId: ourSid });
|
|
196
|
-
addMessage(resolvedSid, "user", JSON.stringify({ text: `[Agent: ${agentDef.title}] ${agentDef.goal}` }), null);
|
|
196
|
+
await addMessage(resolvedSid, "user", JSON.stringify({ text: `[Agent: ${agentDef.title}] ${agentDef.goal}` }), null);
|
|
197
197
|
continue;
|
|
198
198
|
}
|
|
199
199
|
|
|
@@ -204,7 +204,7 @@ export async function runAgent({
|
|
|
204
204
|
lastAssistantText += (lastAssistantText ? "\n\n" : "") + block.text;
|
|
205
205
|
agentSend({ type: "text", text: block.text });
|
|
206
206
|
if (resolvedSid) {
|
|
207
|
-
addMessage(resolvedSid, "assistant", JSON.stringify({ text: block.text }), null);
|
|
207
|
+
await addMessage(resolvedSid, "assistant", JSON.stringify({ text: block.text }), null);
|
|
208
208
|
}
|
|
209
209
|
} else if (block.type === "tool_use") {
|
|
210
210
|
turnCount++;
|
|
@@ -220,7 +220,7 @@ export async function runAgent({
|
|
|
220
220
|
: "",
|
|
221
221
|
});
|
|
222
222
|
if (resolvedSid) {
|
|
223
|
-
addMessage(resolvedSid, "tool", JSON.stringify({ id: block.id, name: block.name, input: block.input }), null);
|
|
223
|
+
await addMessage(resolvedSid, "tool", JSON.stringify({ id: block.id, name: block.name, input: block.input }), null);
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
}
|
|
@@ -242,7 +242,7 @@ export async function runAgent({
|
|
|
242
242
|
isError: block.is_error || false,
|
|
243
243
|
});
|
|
244
244
|
if (resolvedSid) {
|
|
245
|
-
addMessage(resolvedSid, "tool_result", JSON.stringify({
|
|
245
|
+
await addMessage(resolvedSid, "tool_result", JSON.stringify({
|
|
246
246
|
toolUseId: block.tool_use_id,
|
|
247
247
|
content: text.slice(0, 10000),
|
|
248
248
|
isError: block.is_error || false,
|
|
@@ -266,7 +266,7 @@ export async function runAgent({
|
|
|
266
266
|
const resultModel = Object.keys(sdkMsg.modelUsage || {})[0] || sessionModel;
|
|
267
267
|
|
|
268
268
|
if (resolvedSid) {
|
|
269
|
-
addCost(resolvedSid, costUsd, durationMs, numTurns, inputTokens, outputTokens, {
|
|
269
|
+
await addCost(resolvedSid, costUsd, durationMs, numTurns, inputTokens, outputTokens, {
|
|
270
270
|
model: resultModel,
|
|
271
271
|
stopReason: sdkMsg.subtype,
|
|
272
272
|
isError: 0,
|
|
@@ -280,7 +280,7 @@ export async function runAgent({
|
|
|
280
280
|
duration_ms: durationMs,
|
|
281
281
|
num_turns: numTurns,
|
|
282
282
|
cost_usd: costUsd,
|
|
283
|
-
totalCost: getTotalCost(),
|
|
283
|
+
totalCost: await getTotalCost(),
|
|
284
284
|
input_tokens: inputTokens,
|
|
285
285
|
output_tokens: outputTokens,
|
|
286
286
|
cache_read_tokens: cacheReadTokens,
|
|
@@ -301,11 +301,11 @@ export async function runAgent({
|
|
|
301
301
|
|
|
302
302
|
// Record completion for monitoring
|
|
303
303
|
try {
|
|
304
|
-
recordAgentRunComplete(monitorRunId, agentId, 'completed', numTurns, costUsd, durationMs, inputTokens, outputTokens);
|
|
304
|
+
await recordAgentRunComplete(monitorRunId, agentId, 'completed', numTurns, costUsd, durationMs, inputTokens, outputTokens);
|
|
305
305
|
} catch (e) { /* ignore */ }
|
|
306
306
|
|
|
307
307
|
// Log notification
|
|
308
|
-
logNotification('agent', `Agent "${agentDef.title}" completed`,
|
|
308
|
+
await logNotification('agent', `Agent "${agentDef.title}" completed`,
|
|
309
309
|
`${numTurns} turns · $${costUsd.toFixed(4)} · ${(durationMs / 1000).toFixed(1)}s`,
|
|
310
310
|
JSON.stringify({ costUsd, durationMs, inputTokens, outputTokens, turns: numTurns }),
|
|
311
311
|
resolvedSid, agentId);
|
|
@@ -315,7 +315,7 @@ export async function runAgent({
|
|
|
315
315
|
const summary = lastAssistantText.length > 4000
|
|
316
316
|
? lastAssistantText.slice(0, 4000) + "\n\n[truncated]"
|
|
317
317
|
: lastAssistantText;
|
|
318
|
-
setAgentContext(runId, agentId, "output", summary);
|
|
318
|
+
await setAgentContext(runId, agentId, "output", summary);
|
|
319
319
|
}
|
|
320
320
|
} else if (sdkMsg.subtype?.startsWith("error")) {
|
|
321
321
|
const errMsg = sdkMsg.errors?.join(", ") || "Unknown error";
|
|
@@ -329,14 +329,14 @@ export async function runAgent({
|
|
|
329
329
|
const resultModel = Object.keys(sdkMsg.modelUsage || {})[0] || sessionModel;
|
|
330
330
|
|
|
331
331
|
if (resolvedSid) {
|
|
332
|
-
addCost(resolvedSid, costUsd, durationMs, numTurns, inputTokens, outputTokens, {
|
|
332
|
+
await addCost(resolvedSid, costUsd, durationMs, numTurns, inputTokens, outputTokens, {
|
|
333
333
|
model: resultModel,
|
|
334
334
|
stopReason: sdkMsg.subtype,
|
|
335
335
|
isError: 1,
|
|
336
336
|
cacheReadTokens,
|
|
337
337
|
cacheCreationTokens,
|
|
338
338
|
});
|
|
339
|
-
addMessage(resolvedSid, "error", JSON.stringify({ error: errMsg, subtype: sdkMsg.subtype }), null);
|
|
339
|
+
await addMessage(resolvedSid, "error", JSON.stringify({ error: errMsg, subtype: sdkMsg.subtype }), null);
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
lastAgentMetrics = { durationMs, costUsd, inputTokens, outputTokens, model: resultModel, turns: numTurns, isError: true, error: errMsg };
|
|
@@ -345,11 +345,11 @@ export async function runAgent({
|
|
|
345
345
|
|
|
346
346
|
// Record error for monitoring
|
|
347
347
|
try {
|
|
348
|
-
recordAgentRunComplete(monitorRunId, agentId, 'error', numTurns, costUsd, durationMs, inputTokens, outputTokens, errMsg);
|
|
348
|
+
await recordAgentRunComplete(monitorRunId, agentId, 'error', numTurns, costUsd, durationMs, inputTokens, outputTokens, errMsg);
|
|
349
349
|
} catch (e) { /* ignore */ }
|
|
350
350
|
|
|
351
351
|
// Log error notification
|
|
352
|
-
logNotification('error', `Agent "${agentDef.title}" failed`,
|
|
352
|
+
await logNotification('error', `Agent "${agentDef.title}" failed`,
|
|
353
353
|
errMsg.slice(0, 200),
|
|
354
354
|
JSON.stringify({ costUsd, durationMs, error: errMsg }),
|
|
355
355
|
resolvedSid, agentId);
|
|
@@ -361,11 +361,11 @@ export async function runAgent({
|
|
|
361
361
|
if (err.name === "AbortError") {
|
|
362
362
|
agentSend({ type: "agent_aborted", agentId, turn: turnCount });
|
|
363
363
|
agentSend({ type: "aborted" });
|
|
364
|
-
try { recordAgentRunComplete(monitorRunId, agentId, 'aborted', turnCount, 0, 0, 0, 0, 'Aborted'); } catch (e) { /* ignore */ }
|
|
364
|
+
try { await recordAgentRunComplete(monitorRunId, agentId, 'aborted', turnCount, 0, 0, 0, 0, 'Aborted'); } catch (e) { /* ignore */ }
|
|
365
365
|
} else {
|
|
366
366
|
agentSend({ type: "agent_error", agentId, error: err.message, turn: turnCount });
|
|
367
367
|
agentSend({ type: "error", error: err.message });
|
|
368
|
-
try { recordAgentRunComplete(monitorRunId, agentId, 'error', turnCount, 0, 0, 0, 0, err.message); } catch (e) { /* ignore */ }
|
|
368
|
+
try { await recordAgentRunComplete(monitorRunId, agentId, 'error', turnCount, 0, 0, 0, 0, err.message); } catch (e) { /* ignore */ }
|
|
369
369
|
}
|
|
370
370
|
throw err; // Re-throw so callers (chains, DAGs) know the agent failed
|
|
371
371
|
} finally {
|
|
@@ -413,8 +413,8 @@ export async function runAgent({
|
|
|
413
413
|
// Auto-capture memories from agent output
|
|
414
414
|
if (cwd && lastAssistantText) {
|
|
415
415
|
try {
|
|
416
|
-
const explicitCount = saveExplicitMemories(cwd, lastAssistantText, resolvedSid);
|
|
417
|
-
const autoCount = captureMemories(cwd, lastAssistantText, resolvedSid, agentId);
|
|
416
|
+
const explicitCount = await saveExplicitMemories(cwd, lastAssistantText, resolvedSid);
|
|
417
|
+
const autoCount = await captureMemories(cwd, lastAssistantText, resolvedSid, agentId);
|
|
418
418
|
const totalCaptured = explicitCount + autoCount;
|
|
419
419
|
if (totalCaptured > 0) {
|
|
420
420
|
console.log(`Captured ${totalCaptured} memories (${explicitCount} explicit, ${autoCount} auto) from agent ${agentId}`);
|
|
@@ -107,7 +107,7 @@ export function extractMemories(text) {
|
|
|
107
107
|
* @param {string|null} agentId - Source agent ID (for agent runs)
|
|
108
108
|
* @returns {number} Number of new memories saved
|
|
109
109
|
*/
|
|
110
|
-
export function captureMemories(projectPath, assistantText, sessionId = null, agentId = null) {
|
|
110
|
+
export async function captureMemories(projectPath, assistantText, sessionId = null, agentId = null) {
|
|
111
111
|
if (!projectPath || !assistantText) return 0;
|
|
112
112
|
|
|
113
113
|
const extracted = extractMemories(assistantText);
|
|
@@ -115,7 +115,7 @@ export function captureMemories(projectPath, assistantText, sessionId = null, ag
|
|
|
115
115
|
|
|
116
116
|
for (const { category, content } of extracted) {
|
|
117
117
|
try {
|
|
118
|
-
const result = createMemory(projectPath, category, content, sessionId, agentId);
|
|
118
|
+
const result = await createMemory(projectPath, category, content, sessionId, agentId);
|
|
119
119
|
if (!result.isDuplicate) saved++;
|
|
120
120
|
} catch {
|
|
121
121
|
// Ignore individual save errors
|
|
@@ -129,10 +129,10 @@ export function captureMemories(projectPath, assistantText, sessionId = null, ag
|
|
|
129
129
|
* Run memory maintenance for a project.
|
|
130
130
|
* Call this on session start to decay stale memories and clean expired ones.
|
|
131
131
|
*/
|
|
132
|
-
export function runMaintenance(projectPath) {
|
|
132
|
+
export async function runMaintenance(projectPath) {
|
|
133
133
|
if (!projectPath) return;
|
|
134
134
|
try {
|
|
135
|
-
maintainMemories(projectPath);
|
|
135
|
+
await maintainMemories(projectPath);
|
|
136
136
|
} catch {
|
|
137
137
|
// Non-critical — don't break session startup
|
|
138
138
|
}
|
|
@@ -36,11 +36,11 @@ function dedup(memories) {
|
|
|
36
36
|
* @param {string|null} userMessage - Current user message for relevance matching
|
|
37
37
|
* @returns {{ prompt: string|null, count: number }}
|
|
38
38
|
*/
|
|
39
|
-
export function buildMemoryPrompt(projectPath, limit = 10, userMessage = null) {
|
|
39
|
+
export async function buildMemoryPrompt(projectPath, limit = 10, userMessage = null) {
|
|
40
40
|
if (!projectPath) return { prompt: null, count: 0 };
|
|
41
41
|
|
|
42
42
|
// Get top memories by relevance score
|
|
43
|
-
let memories = getTopMemories(projectPath, limit);
|
|
43
|
+
let memories = await getTopMemories(projectPath, limit);
|
|
44
44
|
|
|
45
45
|
// If we have a user message, also search for query-relevant memories
|
|
46
46
|
if (userMessage && userMessage.length > 10) {
|
|
@@ -53,7 +53,7 @@ export function buildMemoryPrompt(projectPath, limit = 10, userMessage = null) {
|
|
|
53
53
|
.filter(w => w.length > 2 && !stopWords.has(w));
|
|
54
54
|
|
|
55
55
|
if (keywords.length > 0) {
|
|
56
|
-
const queryRelevant = searchMemories(projectPath, keywords.join(' '), limit);
|
|
56
|
+
const queryRelevant = await searchMemories(projectPath, keywords.join(' '), limit);
|
|
57
57
|
memories = dedup([...memories, ...queryRelevant]);
|
|
58
58
|
}
|
|
59
59
|
} catch {
|
|
@@ -68,7 +68,7 @@ export function buildMemoryPrompt(projectPath, limit = 10, userMessage = null) {
|
|
|
68
68
|
|
|
69
69
|
// Touch each memory to boost relevance on access
|
|
70
70
|
for (const m of memories) {
|
|
71
|
-
touchMemory(m.id);
|
|
71
|
+
await touchMemory(m.id);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
let prompt = `## Project Memory (persistent knowledge from previous sessions)\n`;
|
|
@@ -100,14 +100,14 @@ export function buildMemoryPrompt(projectPath, limit = 10, userMessage = null) {
|
|
|
100
100
|
/**
|
|
101
101
|
* Build a shorter memory section for agent prompts (tighter budget).
|
|
102
102
|
*/
|
|
103
|
-
export function buildAgentMemoryPrompt(projectPath, limit = 8) {
|
|
103
|
+
export async function buildAgentMemoryPrompt(projectPath, limit = 8) {
|
|
104
104
|
if (!projectPath) return null;
|
|
105
105
|
|
|
106
|
-
const memories = getTopMemories(projectPath, limit);
|
|
106
|
+
const memories = await getTopMemories(projectPath, limit);
|
|
107
107
|
if (!memories || memories.length === 0) return null;
|
|
108
108
|
|
|
109
109
|
for (const m of memories) {
|
|
110
|
-
touchMemory(m.id);
|
|
110
|
+
await touchMemory(m.id);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
let prompt = `## Prior Knowledge\n`;
|
|
@@ -157,7 +157,7 @@ export function parseMemoryBlocks(text) {
|
|
|
157
157
|
* @param {string|null} sessionId
|
|
158
158
|
* @returns {number} count of saved memories
|
|
159
159
|
*/
|
|
160
|
-
export function saveExplicitMemories(projectPath, assistantText, sessionId = null) {
|
|
160
|
+
export async function saveExplicitMemories(projectPath, assistantText, sessionId = null) {
|
|
161
161
|
if (!projectPath || !assistantText) return 0;
|
|
162
162
|
|
|
163
163
|
const blocks = parseMemoryBlocks(assistantText);
|
|
@@ -165,7 +165,7 @@ export function saveExplicitMemories(projectPath, assistantText, sessionId = nul
|
|
|
165
165
|
|
|
166
166
|
for (const { category, content } of blocks) {
|
|
167
167
|
try {
|
|
168
|
-
const result = createMemory(projectPath, category, content, sessionId, null);
|
|
168
|
+
const result = await createMemory(projectPath, category, content, sessionId, null);
|
|
169
169
|
if (!result.isDuplicate) saved++;
|
|
170
170
|
} catch {
|
|
171
171
|
// Ignore individual save errors
|
|
@@ -188,7 +188,7 @@ export function saveExplicitMemories(projectPath, assistantText, sessionId = nul
|
|
|
188
188
|
* @param {string|null} sessionId
|
|
189
189
|
* @returns {{ saved: boolean, content: string, category: string }|null}
|
|
190
190
|
*/
|
|
191
|
-
export function parseRememberCommand(message, projectPath, sessionId = null) {
|
|
191
|
+
export async function parseRememberCommand(message, projectPath, sessionId = null) {
|
|
192
192
|
if (!message || !projectPath) return null;
|
|
193
193
|
|
|
194
194
|
const trimmed = message.trim();
|
|
@@ -213,7 +213,7 @@ export function parseRememberCommand(message, projectPath, sessionId = null) {
|
|
|
213
213
|
const content = text.slice(0, 300);
|
|
214
214
|
|
|
215
215
|
try {
|
|
216
|
-
const result = createMemory(projectPath, category, content, sessionId, null);
|
|
216
|
+
const result = await createMemory(projectPath, category, content, sessionId, null);
|
|
217
217
|
return { saved: !result.isDuplicate, content, category };
|
|
218
218
|
} catch {
|
|
219
219
|
return null;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { query } from "@anthropic-ai/claude-code";
|
|
9
9
|
import { execPath } from "process";
|
|
10
|
-
import { listMemories,
|
|
10
|
+
import { listMemories, getDb } from "../db.js";
|
|
11
11
|
|
|
12
12
|
const VALID_CATEGORIES = new Set(["convention", "decision", "discovery", "warning"]);
|
|
13
13
|
|
|
@@ -163,7 +163,7 @@ function parseOptimizerOutput(text) {
|
|
|
163
163
|
*/
|
|
164
164
|
export async function optimizeMemories(projectPath, onProgress = () => {}) {
|
|
165
165
|
// 1. Load all memories
|
|
166
|
-
const allMemories = listMemories(projectPath);
|
|
166
|
+
const allMemories = await listMemories(projectPath);
|
|
167
167
|
if (!allMemories.length) {
|
|
168
168
|
return { preview: { before: 0, after: 0, summary: "No memories to optimize." } };
|
|
169
169
|
}
|
|
@@ -251,29 +251,33 @@ export async function optimizeMemories(projectPath, onProgress = () => {}) {
|
|
|
251
251
|
* @param {Array<{category: string, content: string}>} optimized
|
|
252
252
|
* @returns {{ deleted: number, created: number }}
|
|
253
253
|
*/
|
|
254
|
-
export function applyOptimization(projectPath, optimized) {
|
|
254
|
+
export async function applyOptimization(projectPath, optimized) {
|
|
255
|
+
const existing = await listMemories(projectPath);
|
|
255
256
|
const db = getDb();
|
|
256
257
|
|
|
257
|
-
//
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
258
|
+
// Atomic transaction — deletes + inserts succeed or fail together
|
|
259
|
+
const applyTxn = db.transaction((existingRows, newRows) => {
|
|
260
|
+
const del = db.prepare(`DELETE FROM memories WHERE id = ?`);
|
|
261
|
+
const ins = db.prepare(
|
|
262
|
+
`INSERT OR IGNORE INTO memories (project_path, category, content, content_hash, source_session_id, source_agent_id)
|
|
263
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
for (const m of existingRows) {
|
|
267
|
+
del.run(m.id);
|
|
263
268
|
}
|
|
264
269
|
|
|
265
|
-
// 2. Insert optimized memories
|
|
266
270
|
let created = 0;
|
|
267
|
-
for (const { category, content } of
|
|
271
|
+
for (const { category, content } of newRows) {
|
|
268
272
|
if (content && content.trim()) {
|
|
269
273
|
const cat = VALID_CATEGORIES.has(category) ? category : "discovery";
|
|
270
|
-
|
|
274
|
+
ins.run(projectPath, cat, content.trim(), null, null, "optimizer");
|
|
271
275
|
created++;
|
|
272
276
|
}
|
|
273
277
|
}
|
|
274
|
-
|
|
275
|
-
return { deleted: existing.length, created };
|
|
278
|
+
return created;
|
|
276
279
|
});
|
|
277
280
|
|
|
278
|
-
|
|
281
|
+
const created = applyTxn(existing, optimized);
|
|
282
|
+
return { deleted: existing.length, created };
|
|
279
283
|
}
|
|
@@ -14,14 +14,14 @@ function broadcast(payload) {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function logNotification(type, title, body = null, metadata = null, sourceSessionId = null, sourceAgentId = null) {
|
|
18
|
-
const notification = createNotification(type, title, body, metadata, sourceSessionId, sourceAgentId);
|
|
19
|
-
const unreadCount = getUnreadNotificationCount();
|
|
17
|
+
export async function logNotification(type, title, body = null, metadata = null, sourceSessionId = null, sourceAgentId = null) {
|
|
18
|
+
const notification = await createNotification(type, title, body, metadata, sourceSessionId, sourceAgentId);
|
|
19
|
+
const unreadCount = await getUnreadNotificationCount();
|
|
20
20
|
broadcast({ type: "notification:new", notification, unreadCount });
|
|
21
21
|
return notification;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function broadcastReadUpdate(ids) {
|
|
25
|
-
const unreadCount = getUnreadNotificationCount();
|
|
24
|
+
export async function broadcastReadUpdate(ids) {
|
|
25
|
+
const unreadCount = await getUnreadNotificationCount();
|
|
26
26
|
broadcast({ type: "notification:read", ids, unreadCount });
|
|
27
27
|
}
|
package/server/orchestrator.js
CHANGED
|
@@ -68,10 +68,10 @@ For each sub-task you want to delegate, output a fenced code block with the lang
|
|
|
68
68
|
${task}`;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
function buildOrchestratorPromptWithMemory(task, agents, cwd) {
|
|
71
|
+
async function buildOrchestratorPromptWithMemory(task, agents, cwd) {
|
|
72
72
|
let prompt = buildOrchestratorPrompt(task, agents);
|
|
73
73
|
if (cwd) {
|
|
74
|
-
const memPrompt = buildAgentMemoryPrompt(cwd, 6);
|
|
74
|
+
const memPrompt = await buildAgentMemoryPrompt(cwd, 6);
|
|
75
75
|
if (memPrompt) {
|
|
76
76
|
prompt += '\n\n' + memPrompt;
|
|
77
77
|
}
|
|
@@ -186,7 +186,7 @@ export async function runOrchestrator({
|
|
|
186
186
|
orchSend({ type: "orchestrator_phase", phase: "planning" });
|
|
187
187
|
|
|
188
188
|
try {
|
|
189
|
-
const prompt = buildOrchestratorPromptWithMemory(task, agents, cwd);
|
|
189
|
+
const prompt = await buildOrchestratorPromptWithMemory(task, agents, cwd);
|
|
190
190
|
const q = query({ prompt, options: plannerOpts });
|
|
191
191
|
|
|
192
192
|
for await (const sdkMsg of q) {
|
|
@@ -198,20 +198,20 @@ export async function runOrchestrator({
|
|
|
198
198
|
resolvedSid = ourSid;
|
|
199
199
|
sessionIds.set(ourSid, claudeSessionId);
|
|
200
200
|
|
|
201
|
-
if (!getSession(ourSid)) {
|
|
202
|
-
createSession(
|
|
201
|
+
if (!await getSession(ourSid)) {
|
|
202
|
+
await createSession(
|
|
203
203
|
ourSid,
|
|
204
204
|
claudeSessionId,
|
|
205
205
|
projectName || "Orchestrator",
|
|
206
206
|
cwd || "",
|
|
207
207
|
);
|
|
208
|
-
updateSessionTitle(ourSid, `Orchestrator: ${task.slice(0, 60)}`);
|
|
208
|
+
await updateSessionTitle(ourSid, `Orchestrator: ${task.slice(0, 60)}`);
|
|
209
209
|
} else {
|
|
210
|
-
updateClaudeSessionId(ourSid, claudeSessionId);
|
|
210
|
+
await updateClaudeSessionId(ourSid, claudeSessionId);
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
orchSend({ type: "session", sessionId: ourSid });
|
|
214
|
-
addMessage(
|
|
214
|
+
await addMessage(
|
|
215
215
|
resolvedSid,
|
|
216
216
|
"user",
|
|
217
217
|
JSON.stringify({ text: `[Orchestrator] ${task}` }),
|
|
@@ -226,7 +226,7 @@ export async function runOrchestrator({
|
|
|
226
226
|
plannerText += block.text;
|
|
227
227
|
orchSend({ type: "text", text: block.text });
|
|
228
228
|
if (resolvedSid) {
|
|
229
|
-
addMessage(
|
|
229
|
+
await addMessage(
|
|
230
230
|
resolvedSid,
|
|
231
231
|
"assistant",
|
|
232
232
|
JSON.stringify({ text: block.text }),
|
|
@@ -252,7 +252,7 @@ export async function runOrchestrator({
|
|
|
252
252
|
Object.keys(sdkMsg.modelUsage || {})[0] || null;
|
|
253
253
|
|
|
254
254
|
if (resolvedSid) {
|
|
255
|
-
addCost(
|
|
255
|
+
await addCost(
|
|
256
256
|
resolvedSid,
|
|
257
257
|
costUsd,
|
|
258
258
|
durationMs,
|
|
@@ -274,7 +274,7 @@ export async function runOrchestrator({
|
|
|
274
274
|
duration_ms: durationMs,
|
|
275
275
|
num_turns: numTurns,
|
|
276
276
|
cost_usd: costUsd,
|
|
277
|
-
totalCost: getTotalCost(),
|
|
277
|
+
totalCost: await getTotalCost(),
|
|
278
278
|
input_tokens: inputTokens,
|
|
279
279
|
output_tokens: outputTokens,
|
|
280
280
|
model: resultModel,
|
|
@@ -375,7 +375,7 @@ export async function runOrchestrator({
|
|
|
375
375
|
if (result?.claudeSessionId) chainResumeId = result.claudeSessionId;
|
|
376
376
|
|
|
377
377
|
// Read context that the agent stored
|
|
378
|
-
const ctx = getAllAgentContext(runId).find(
|
|
378
|
+
const ctx = (await getAllAgentContext(runId)).find(
|
|
379
379
|
(c) => c.agent_id === agentDef.id,
|
|
380
380
|
);
|
|
381
381
|
|
|
@@ -439,7 +439,7 @@ export async function runOrchestrator({
|
|
|
439
439
|
if (block.type === "text" && block.text) {
|
|
440
440
|
orchSend({ type: "text", text: block.text });
|
|
441
441
|
if (resolvedSid) {
|
|
442
|
-
addMessage(
|
|
442
|
+
await addMessage(
|
|
443
443
|
resolvedSid,
|
|
444
444
|
"assistant",
|
|
445
445
|
JSON.stringify({ text: block.text }),
|
|
@@ -465,7 +465,7 @@ export async function runOrchestrator({
|
|
|
465
465
|
Object.keys(sdkMsg.modelUsage || {})[0] || null;
|
|
466
466
|
|
|
467
467
|
if (resolvedSid) {
|
|
468
|
-
addCost(
|
|
468
|
+
await addCost(
|
|
469
469
|
resolvedSid,
|
|
470
470
|
costUsd,
|
|
471
471
|
durationMs,
|
|
@@ -487,7 +487,7 @@ export async function runOrchestrator({
|
|
|
487
487
|
duration_ms: durationMs,
|
|
488
488
|
num_turns: numTurns,
|
|
489
489
|
cost_usd: costUsd,
|
|
490
|
-
totalCost: getTotalCost(),
|
|
490
|
+
totalCost: await getTotalCost(),
|
|
491
491
|
input_tokens: inputTokens,
|
|
492
492
|
output_tokens: outputTokens,
|
|
493
493
|
model: resultModel,
|
package/server/push-sender.js
CHANGED
|
@@ -9,7 +9,7 @@ export function initPushSender(webpush) {
|
|
|
9
9
|
export async function sendPushNotification(title, body, tag) {
|
|
10
10
|
if (!webpushInstance) return;
|
|
11
11
|
|
|
12
|
-
const subs = getAllPushSubscriptions();
|
|
12
|
+
const subs = await getAllPushSubscriptions();
|
|
13
13
|
if (!subs.length) return;
|
|
14
14
|
|
|
15
15
|
const payload = JSON.stringify({ title, body, tag });
|
|
@@ -23,7 +23,7 @@ export async function sendPushNotification(title, body, tag) {
|
|
|
23
23
|
);
|
|
24
24
|
} catch (err) {
|
|
25
25
|
if (err.statusCode === 404 || err.statusCode === 410) {
|
|
26
|
-
deletePushSubscription(sub.endpoint);
|
|
26
|
+
await deletePushSubscription(sub.endpoint);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
})
|
package/server/routes/agents.js
CHANGED
|
@@ -46,9 +46,9 @@ function slugify(text) {
|
|
|
46
46
|
|
|
47
47
|
// ── Agent Context (shared memory) ──
|
|
48
48
|
|
|
49
|
-
router.get("/context/:runId", (req, res) => {
|
|
49
|
+
router.get("/context/:runId", async (req, res) => {
|
|
50
50
|
try {
|
|
51
|
-
const rows = getAllAgentContext(req.params.runId);
|
|
51
|
+
const rows = await getAllAgentContext(req.params.runId);
|
|
52
52
|
res.json(rows);
|
|
53
53
|
} catch (err) {
|
|
54
54
|
res.status(500).json({ error: err.message });
|