claudeck 1.3.0 → 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
  }
@@ -158,6 +158,7 @@ export async function runOrchestrator({
158
158
  abortController,
159
159
  maxTurns: 3, // Planner should just think, not use many tools
160
160
  executable: execPath,
161
+ settingSources: ["user", "project", "local"],
161
162
  };
162
163
 
163
164
  if (!useBypass && !usePlan) {
@@ -185,7 +186,7 @@ export async function runOrchestrator({
185
186
  orchSend({ type: "orchestrator_phase", phase: "planning" });
186
187
 
187
188
  try {
188
- const prompt = buildOrchestratorPromptWithMemory(task, agents, cwd);
189
+ const prompt = await buildOrchestratorPromptWithMemory(task, agents, cwd);
189
190
  const q = query({ prompt, options: plannerOpts });
190
191
 
191
192
  for await (const sdkMsg of q) {
@@ -197,20 +198,20 @@ export async function runOrchestrator({
197
198
  resolvedSid = ourSid;
198
199
  sessionIds.set(ourSid, claudeSessionId);
199
200
 
200
- if (!getSession(ourSid)) {
201
- createSession(
201
+ if (!await getSession(ourSid)) {
202
+ await createSession(
202
203
  ourSid,
203
204
  claudeSessionId,
204
205
  projectName || "Orchestrator",
205
206
  cwd || "",
206
207
  );
207
- updateSessionTitle(ourSid, `Orchestrator: ${task.slice(0, 60)}`);
208
+ await updateSessionTitle(ourSid, `Orchestrator: ${task.slice(0, 60)}`);
208
209
  } else {
209
- updateClaudeSessionId(ourSid, claudeSessionId);
210
+ await updateClaudeSessionId(ourSid, claudeSessionId);
210
211
  }
211
212
 
212
213
  orchSend({ type: "session", sessionId: ourSid });
213
- addMessage(
214
+ await addMessage(
214
215
  resolvedSid,
215
216
  "user",
216
217
  JSON.stringify({ text: `[Orchestrator] ${task}` }),
@@ -225,7 +226,7 @@ export async function runOrchestrator({
225
226
  plannerText += block.text;
226
227
  orchSend({ type: "text", text: block.text });
227
228
  if (resolvedSid) {
228
- addMessage(
229
+ await addMessage(
229
230
  resolvedSid,
230
231
  "assistant",
231
232
  JSON.stringify({ text: block.text }),
@@ -251,7 +252,7 @@ export async function runOrchestrator({
251
252
  Object.keys(sdkMsg.modelUsage || {})[0] || null;
252
253
 
253
254
  if (resolvedSid) {
254
- addCost(
255
+ await addCost(
255
256
  resolvedSid,
256
257
  costUsd,
257
258
  durationMs,
@@ -273,7 +274,7 @@ export async function runOrchestrator({
273
274
  duration_ms: durationMs,
274
275
  num_turns: numTurns,
275
276
  cost_usd: costUsd,
276
- totalCost: getTotalCost(),
277
+ totalCost: await getTotalCost(),
277
278
  input_tokens: inputTokens,
278
279
  output_tokens: outputTokens,
279
280
  model: resultModel,
@@ -374,7 +375,7 @@ export async function runOrchestrator({
374
375
  if (result?.claudeSessionId) chainResumeId = result.claudeSessionId;
375
376
 
376
377
  // Read context that the agent stored
377
- const ctx = getAllAgentContext(runId).find(
378
+ const ctx = (await getAllAgentContext(runId)).find(
378
379
  (c) => c.agent_id === agentDef.id,
379
380
  );
380
381
 
@@ -438,7 +439,7 @@ export async function runOrchestrator({
438
439
  if (block.type === "text" && block.text) {
439
440
  orchSend({ type: "text", text: block.text });
440
441
  if (resolvedSid) {
441
- addMessage(
442
+ await addMessage(
442
443
  resolvedSid,
443
444
  "assistant",
444
445
  JSON.stringify({ text: block.text }),
@@ -464,7 +465,7 @@ export async function runOrchestrator({
464
465
  Object.keys(sdkMsg.modelUsage || {})[0] || null;
465
466
 
466
467
  if (resolvedSid) {
467
- addCost(
468
+ await addCost(
468
469
  resolvedSid,
469
470
  costUsd,
470
471
  durationMs,
@@ -486,7 +487,7 @@ export async function runOrchestrator({
486
487
  duration_ms: durationMs,
487
488
  num_turns: numTurns,
488
489
  cost_usd: costUsd,
489
- totalCost: getTotalCost(),
490
+ totalCost: await getTotalCost(),
490
491
  input_tokens: inputTokens,
491
492
  output_tokens: outputTokens,
492
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