claudeck 1.3.1 → 1.4.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.
@@ -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;
@@ -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,7 +251,7 @@ 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
255
  const db = getDb();
256
256
 
257
257
  // Run in a transaction for atomicity
@@ -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
  }
@@ -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,
@@ -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
  })
@@ -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 });
@@ -11,11 +11,11 @@ const router = Router();
11
11
  const CATEGORIES = ["convention", "decision", "discovery", "warning"];
12
12
 
13
13
  // List memories for a project
14
- router.get("/", (req, res) => {
14
+ router.get("/", async (req, res) => {
15
15
  try {
16
16
  const { project, category } = req.query;
17
17
  if (!project) return res.status(400).json({ error: "project query param required" });
18
- const memories = listMemories(project, category || null);
18
+ const memories = await listMemories(project, category || null);
19
19
  res.json(memories);
20
20
  } catch (err) {
21
21
  res.status(500).json({ error: err.message });
@@ -23,11 +23,11 @@ router.get("/", (req, res) => {
23
23
  });
24
24
 
25
25
  // Search memories
26
- router.get("/search", (req, res) => {
26
+ router.get("/search", async (req, res) => {
27
27
  try {
28
28
  const { project, q, limit } = req.query;
29
29
  if (!project || !q) return res.status(400).json({ error: "project and q required" });
30
- const results = searchMemories(project, q, Number(limit) || 20);
30
+ const results = await searchMemories(project, q, Number(limit) || 20);
31
31
  res.json(results);
32
32
  } catch (err) {
33
33
  res.status(500).json({ error: err.message });
@@ -35,13 +35,13 @@ router.get("/search", (req, res) => {
35
35
  });
36
36
 
37
37
  // Get top relevant memories (used for prompt injection)
38
- router.get("/top", (req, res) => {
38
+ router.get("/top", async (req, res) => {
39
39
  try {
40
40
  const { project, limit } = req.query;
41
41
  if (!project) return res.status(400).json({ error: "project required" });
42
- const memories = getTopMemories(project, Number(limit) || 10);
42
+ const memories = await getTopMemories(project, Number(limit) || 10);
43
43
  // Touch each memory to boost relevance
44
- for (const m of memories) touchMemory(m.id);
44
+ for (const m of memories) await touchMemory(m.id);
45
45
  res.json(memories);
46
46
  } catch (err) {
47
47
  res.status(500).json({ error: err.message });
@@ -49,12 +49,12 @@ router.get("/top", (req, res) => {
49
49
  });
50
50
 
51
51
  // Get stats
52
- router.get("/stats", (req, res) => {
52
+ router.get("/stats", async (req, res) => {
53
53
  try {
54
54
  const { project } = req.query;
55
55
  if (!project) return res.status(400).json({ error: "project required" });
56
- const stats = getMemoryStats(project);
57
- const counts = getMemoryCounts(project);
56
+ const stats = await getMemoryStats(project);
57
+ const counts = await getMemoryCounts(project);
58
58
  res.json({ ...stats, categories: counts });
59
59
  } catch (err) {
60
60
  res.status(500).json({ error: err.message });
@@ -62,14 +62,14 @@ router.get("/stats", (req, res) => {
62
62
  });
63
63
 
64
64
  // Create a memory
65
- router.post("/", (req, res) => {
65
+ router.post("/", async (req, res) => {
66
66
  try {
67
67
  const { project, category, content, sessionId, agentId } = req.body;
68
68
  if (!project || !content) {
69
69
  return res.status(400).json({ error: "project and content required" });
70
70
  }
71
71
  const cat = CATEGORIES.includes(category) ? category : "discovery";
72
- const info = createMemory(project, cat, content.trim(), sessionId || null, agentId || null);
72
+ const info = await createMemory(project, cat, content.trim(), sessionId || null, agentId || null);
73
73
  res.json({ id: info.lastInsertRowid });
74
74
  } catch (err) {
75
75
  res.status(500).json({ error: err.message });
@@ -77,13 +77,13 @@ router.post("/", (req, res) => {
77
77
  });
78
78
 
79
79
  // Update a memory
80
- router.put("/:id", (req, res) => {
80
+ router.put("/:id", async (req, res) => {
81
81
  try {
82
82
  const id = Number(req.params.id);
83
83
  const { content, category } = req.body;
84
84
  if (!content) return res.status(400).json({ error: "content required" });
85
85
  const cat = CATEGORIES.includes(category) ? category : "discovery";
86
- updateMemory(id, content.trim(), cat);
86
+ await updateMemory(id, content.trim(), cat);
87
87
  res.json({ ok: true });
88
88
  } catch (err) {
89
89
  res.status(500).json({ error: err.message });
@@ -91,10 +91,10 @@ router.put("/:id", (req, res) => {
91
91
  });
92
92
 
93
93
  // Delete a memory
94
- router.delete("/:id", (req, res) => {
94
+ router.delete("/:id", async (req, res) => {
95
95
  try {
96
96
  const id = Number(req.params.id);
97
- deleteMemory(id);
97
+ await deleteMemory(id);
98
98
  res.json({ ok: true });
99
99
  } catch (err) {
100
100
  res.status(500).json({ error: err.message });
@@ -102,11 +102,11 @@ router.delete("/:id", (req, res) => {
102
102
  });
103
103
 
104
104
  // Decay old memories and clean expired
105
- router.post("/maintain", (req, res) => {
105
+ router.post("/maintain", async (req, res) => {
106
106
  try {
107
107
  const { project } = req.body;
108
108
  if (!project) return res.status(400).json({ error: "project required" });
109
- maintainMemories(project);
109
+ await maintainMemories(project);
110
110
  res.json({ ok: true });
111
111
  } catch (err) {
112
112
  res.status(500).json({ error: err.message });
@@ -129,13 +129,13 @@ router.post("/optimize", async (req, res) => {
129
129
  });
130
130
 
131
131
  // Apply optimization — replace memories with optimized set
132
- router.post("/optimize/apply", (req, res) => {
132
+ router.post("/optimize/apply", async (req, res) => {
133
133
  try {
134
134
  const { project, optimized } = req.body;
135
135
  if (!project || !Array.isArray(optimized)) {
136
136
  return res.status(400).json({ error: "project and optimized array required" });
137
137
  }
138
- const result = applyOptimization(project, optimized);
138
+ const result = await applyOptimization(project, optimized);
139
139
  res.json(result);
140
140
  } catch (err) {
141
141
  console.error("Apply optimization error:", err);
@@ -1,32 +1,63 @@
1
1
  import { Router } from "express";
2
- import { getMessages, getMessagesByChatId, getMessagesNoChatId } from "../../db.js";
2
+ import {
3
+ getMessages, getMessagesByChatId, getMessagesNoChatId,
4
+ getRecentMessages, getRecentMessagesByChatId, getRecentMessagesNoChatId,
5
+ getOlderMessages, getOlderMessagesByChatId, getOlderMessagesNoChatId,
6
+ } from "../../db.js";
3
7
 
4
8
  const router = Router();
5
9
 
6
- // Get all messages for a session
7
- router.get("/:id/messages", (req, res) => {
10
+ // Get all messages for a session (supports ?limit=N&before=ID)
11
+ router.get("/:id/messages", async (req, res) => {
8
12
  try {
9
- const messages = getMessages(req.params.id);
13
+ const limit = req.query.limit ? parseInt(req.query.limit, 10) : 0;
14
+ const before = req.query.before ? parseInt(req.query.before, 10) : 0;
15
+ let messages;
16
+ if (limit > 0 && before > 0) {
17
+ messages = await getOlderMessages(req.params.id, before, limit);
18
+ } else if (limit > 0) {
19
+ messages = await getRecentMessages(req.params.id, limit);
20
+ } else {
21
+ messages = await getMessages(req.params.id);
22
+ }
10
23
  res.json(messages);
11
24
  } catch (err) {
12
25
  res.status(500).json({ error: err.message });
13
26
  }
14
27
  });
15
28
 
16
- // Get messages filtered by chatId
17
- router.get("/:id/messages/:chatId", (req, res) => {
29
+ // Get messages filtered by chatId (supports ?limit=N&before=ID)
30
+ router.get("/:id/messages/:chatId", async (req, res) => {
18
31
  try {
19
- const messages = getMessagesByChatId(req.params.id, req.params.chatId);
32
+ const limit = req.query.limit ? parseInt(req.query.limit, 10) : 0;
33
+ const before = req.query.before ? parseInt(req.query.before, 10) : 0;
34
+ let messages;
35
+ if (limit > 0 && before > 0) {
36
+ messages = await getOlderMessagesByChatId(req.params.id, req.params.chatId, before, limit);
37
+ } else if (limit > 0) {
38
+ messages = await getRecentMessagesByChatId(req.params.id, req.params.chatId, limit);
39
+ } else {
40
+ messages = await getMessagesByChatId(req.params.id, req.params.chatId);
41
+ }
20
42
  res.json(messages);
21
43
  } catch (err) {
22
44
  res.status(500).json({ error: err.message });
23
45
  }
24
46
  });
25
47
 
26
- // Get messages where chat_id IS NULL (single-mode)
27
- router.get("/:id/messages-single", (req, res) => {
48
+ // Get messages where chat_id IS NULL (supports ?limit=N&before=ID)
49
+ router.get("/:id/messages-single", async (req, res) => {
28
50
  try {
29
- const messages = getMessagesNoChatId(req.params.id);
51
+ const limit = req.query.limit ? parseInt(req.query.limit, 10) : 0;
52
+ const before = req.query.before ? parseInt(req.query.before, 10) : 0;
53
+ let messages;
54
+ if (limit > 0 && before > 0) {
55
+ messages = await getOlderMessagesNoChatId(req.params.id, before, limit);
56
+ } else if (limit > 0) {
57
+ messages = await getRecentMessagesNoChatId(req.params.id, limit);
58
+ } else {
59
+ messages = await getMessagesNoChatId(req.params.id);
60
+ }
30
61
  res.json(messages);
31
62
  } catch (err) {
32
63
  res.status(500).json({ error: err.message });
@@ -26,67 +26,67 @@ router.get("/vapid-public-key", (req, res) => {
26
26
  res.json({ key: vapidPublicKey });
27
27
  });
28
28
 
29
- router.post("/subscribe", (req, res) => {
29
+ router.post("/subscribe", async (req, res) => {
30
30
  const { endpoint, keys } = req.body;
31
31
  if (!endpoint || !keys?.p256dh || !keys?.auth) {
32
32
  return res.status(400).json({ error: "Invalid subscription" });
33
33
  }
34
- upsertPushSubscription(endpoint, keys.p256dh, keys.auth);
34
+ await upsertPushSubscription(endpoint, keys.p256dh, keys.auth);
35
35
  res.json({ ok: true });
36
36
  });
37
37
 
38
- router.post("/unsubscribe", (req, res) => {
38
+ router.post("/unsubscribe", async (req, res) => {
39
39
  const { endpoint } = req.body;
40
40
  if (!endpoint) {
41
41
  return res.status(400).json({ error: "Missing endpoint" });
42
42
  }
43
- deletePushSubscription(endpoint);
43
+ await deletePushSubscription(endpoint);
44
44
  res.json({ ok: true });
45
45
  });
46
46
 
47
47
  // ── Create notification (from frontend) ───────────────────
48
- router.post("/create", (req, res) => {
48
+ router.post("/create", async (req, res) => {
49
49
  const { type, title, body, metadata, sourceSessionId, sourceAgentId } = req.body;
50
50
  if (!type || !title) {
51
51
  return res.status(400).json({ error: "type and title are required" });
52
52
  }
53
- const notification = logNotification(type, title, body || null, metadata || null, sourceSessionId || null, sourceAgentId || null);
53
+ const notification = await logNotification(type, title, body || null, metadata || null, sourceSessionId || null, sourceAgentId || null);
54
54
  res.json(notification);
55
55
  });
56
56
 
57
57
  // ── Notification history & management ─────────────────────
58
- router.get("/history", (req, res) => {
58
+ router.get("/history", async (req, res) => {
59
59
  const limit = Math.min(parseInt(req.query.limit) || 20, 100);
60
60
  const offset = parseInt(req.query.offset) || 0;
61
61
  const unreadOnly = req.query.unread_only === "true";
62
62
  const type = req.query.type || null;
63
- const items = getNotificationHistory(limit, offset, unreadOnly, type);
63
+ const items = await getNotificationHistory(limit, offset, unreadOnly, type);
64
64
  res.json(items);
65
65
  });
66
66
 
67
- router.get("/unread-count", (_req, res) => {
68
- res.json({ count: getUnreadNotificationCount() });
67
+ router.get("/unread-count", async (_req, res) => {
68
+ res.json({ count: await getUnreadNotificationCount() });
69
69
  });
70
70
 
71
- router.post("/read", (req, res) => {
71
+ router.post("/read", async (req, res) => {
72
72
  const { ids, all, before } = req.body;
73
73
  if (all) {
74
- markAllNotificationsRead();
75
- broadcastReadUpdate([]);
74
+ await markAllNotificationsRead();
75
+ await broadcastReadUpdate([]);
76
76
  } else if (before) {
77
- markNotificationsReadBefore(before);
78
- broadcastReadUpdate([]);
77
+ await markNotificationsReadBefore(before);
78
+ await broadcastReadUpdate([]);
79
79
  } else if (Array.isArray(ids) && ids.length > 0) {
80
- markNotificationsRead(ids);
81
- broadcastReadUpdate(ids);
80
+ await markNotificationsRead(ids);
81
+ await broadcastReadUpdate(ids);
82
82
  } else {
83
83
  return res.status(400).json({ error: "Provide ids, all, or before" });
84
84
  }
85
- res.json({ ok: true, unreadCount: getUnreadNotificationCount() });
85
+ res.json({ ok: true, unreadCount: await getUnreadNotificationCount() });
86
86
  });
87
87
 
88
- router.delete("/old", (_req, res) => {
89
- purgeOldNotifications(90);
88
+ router.delete("/old", async (_req, res) => {
89
+ await purgeOldNotifications(90);
90
90
  res.json({ ok: true });
91
91
  });
92
92