Skip to content

Commit 7760537

Browse files
committed
refactor: close remaining audit findings (#2,#3,#4,#8,#15,#17)
#2: Added WeakMap prepared statement caching to storage-tags.ts and storage-ops.ts for consistency with storage-memory.ts pattern. #3: Extracted insertCompartmentRows/insertFactRows helpers with cached prepared statements, replacing 4 duplicate INSERT sites. #4: Removed dead exports: DreamingConfigSchema, DreamingConfig, getQueueSize, hasPendingOps, parseJsoncSafe, JsoncParseResult, createTagContentResolver and its file. #8: Added Intentional comment documenting single-threaded safety of compartment runner check-then-set pattern. #15: Cached scoped assignments in tag resolver with size-based invalidation — avoids 16k regex iterations on every transform. #17: Threaded modelKey through scheduler.shouldExecute() so per-model execute_threshold_percentage applies to queue execution decisions.
1 parent 3f0ea86 commit 7760537

15 files changed

Lines changed: 189 additions & 300 deletions

File tree

src/config/schema/magic-context.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,6 @@ export const DEFAULT_DREAMER_TASKS: DreamingTask[] = [
2828
"improve",
2929
];
3030

31-
export const DreamingConfigSchema = z
32-
.object({
33-
/** Enable dreamer (default: false) */
34-
enabled: z.boolean().default(false),
35-
/** Scheduled window for overnight dreaming (e.g. "02:00-06:00") */
36-
schedule: z.string().default("02:00-06:00"),
37-
/** Maximum runtime per dream session in minutes (default: 120) */
38-
max_runtime_minutes: z.number().min(10).default(120),
39-
/** Tasks to run during dreaming, in order (default: consolidate, verify, archive-stale, improve) */
40-
tasks: z.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS),
41-
/** Minutes allocated per task before moving to next (default: 20) */
42-
task_timeout_minutes: z.number().min(5).default(20),
43-
})
44-
.default({
45-
enabled: false,
46-
schedule: "02:00-06:00",
47-
max_runtime_minutes: 120,
48-
tasks: DEFAULT_DREAMER_TASKS,
49-
task_timeout_minutes: 20,
50-
});
51-
5231
/** Combined dreamer agent + scheduling configuration */
5332
export const DreamerConfigSchema = AgentOverrideConfigSchema.merge(
5433
z.object({
@@ -120,7 +99,6 @@ export const EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data)
12099
});
121100

122101
export type EmbeddingConfig = z.infer<typeof EmbeddingConfigSchema>;
123-
export type DreamingConfig = z.infer<typeof DreamingConfigSchema>;
124102

125103
export interface MagicContextConfig {
126104
enabled: boolean;

src/features/magic-context/compartment-storage.ts

Lines changed: 68 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
import type { Database } from "bun:sqlite";
22

3+
type PreparedStatement = ReturnType<Database["prepare"]>;
4+
5+
const insertCompartmentStatements = new WeakMap<Database, PreparedStatement>();
6+
const insertFactStatements = new WeakMap<Database, PreparedStatement>();
7+
8+
function getInsertCompartmentStatement(db: Database): PreparedStatement {
9+
let stmt = insertCompartmentStatements.get(db);
10+
if (!stmt) {
11+
stmt = db.prepare(
12+
"INSERT INTO compartments (session_id, sequence, start_message, end_message, start_message_id, end_message_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
13+
);
14+
insertCompartmentStatements.set(db, stmt);
15+
}
16+
return stmt;
17+
}
18+
19+
function getInsertFactStatement(db: Database): PreparedStatement {
20+
let stmt = insertFactStatements.get(db);
21+
if (!stmt) {
22+
stmt = db.prepare(
23+
"INSERT INTO session_facts (session_id, category, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
24+
);
25+
insertFactStatements.set(db, stmt);
26+
}
27+
return stmt;
28+
}
29+
330
export interface Compartment {
431
id: number;
532
sessionId: string;
@@ -84,6 +111,40 @@ export interface CompartmentInput {
84111
content: string;
85112
}
86113

114+
function insertCompartmentRows(
115+
db: Database,
116+
sessionId: string,
117+
compartments: CompartmentInput[],
118+
now: number,
119+
): void {
120+
const stmt = getInsertCompartmentStatement(db);
121+
for (const compartment of compartments) {
122+
stmt.run(
123+
sessionId,
124+
compartment.sequence,
125+
compartment.startMessage,
126+
compartment.endMessage,
127+
compartment.startMessageId,
128+
compartment.endMessageId,
129+
compartment.title,
130+
compartment.content,
131+
now,
132+
);
133+
}
134+
}
135+
136+
function insertFactRows(
137+
db: Database,
138+
sessionId: string,
139+
facts: Array<{ category: string; content: string }>,
140+
now: number,
141+
): void {
142+
const stmt = getInsertFactStatement(db);
143+
for (const fact of facts) {
144+
stmt.run(sessionId, fact.category, fact.content, now, now);
145+
}
146+
}
147+
87148
function toCompartment(row: CompartmentRow): Compartment {
88149
return {
89150
id: row.id,
@@ -135,22 +196,7 @@ export function replaceAllCompartments(
135196
const now = Date.now();
136197
db.transaction(() => {
137198
db.prepare("DELETE FROM compartments WHERE session_id = ?").run(sessionId);
138-
const stmt = db.prepare(
139-
"INSERT INTO compartments (session_id, sequence, start_message, end_message, start_message_id, end_message_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
140-
);
141-
for (const c of compartments) {
142-
stmt.run(
143-
sessionId,
144-
c.sequence,
145-
c.startMessage,
146-
c.endMessage,
147-
c.startMessageId,
148-
c.endMessageId,
149-
c.title,
150-
c.content,
151-
now,
152-
);
153-
}
199+
insertCompartmentRows(db, sessionId, compartments, now);
154200
})();
155201
}
156202

@@ -167,22 +213,7 @@ export function appendCompartments(
167213
if (compartments.length === 0) return;
168214
const now = Date.now();
169215
db.transaction(() => {
170-
const stmt = db.prepare(
171-
"INSERT INTO compartments (session_id, sequence, start_message, end_message, start_message_id, end_message_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
172-
);
173-
for (const c of compartments) {
174-
stmt.run(
175-
sessionId,
176-
c.sequence,
177-
c.startMessage,
178-
c.endMessage,
179-
c.startMessageId,
180-
c.endMessageId,
181-
c.title,
182-
c.content,
183-
now,
184-
);
185-
}
216+
insertCompartmentRows(db, sessionId, compartments, now);
186217
})();
187218
}
188219

@@ -199,12 +230,7 @@ export function replaceSessionFacts(
199230
const now = Date.now();
200231
db.transaction(() => {
201232
db.prepare("DELETE FROM session_facts WHERE session_id = ?").run(sessionId);
202-
const stmt = db.prepare(
203-
"INSERT INTO session_facts (session_id, category, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
204-
);
205-
for (const f of facts) {
206-
stmt.run(sessionId, f.category, f.content, now, now);
207-
}
233+
insertFactRows(db, sessionId, facts, now);
208234
// Clear cached memory block so next injection renders fresh
209235
db.prepare(
210236
"UPDATE session_meta SET memory_block_cache = '', memory_block_count = 0 WHERE session_id = ?",
@@ -231,29 +257,8 @@ export function replaceAllCompartmentState(
231257
db.prepare("DELETE FROM compartments WHERE session_id = ?").run(sessionId);
232258
db.prepare("DELETE FROM session_facts WHERE session_id = ?").run(sessionId);
233259

234-
const compartmentStmt = db.prepare(
235-
"INSERT INTO compartments (session_id, sequence, start_message, end_message, start_message_id, end_message_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
236-
);
237-
for (const c of compartments) {
238-
compartmentStmt.run(
239-
sessionId,
240-
c.sequence,
241-
c.startMessage,
242-
c.endMessage,
243-
c.startMessageId,
244-
c.endMessageId,
245-
c.title,
246-
c.content,
247-
now,
248-
);
249-
}
250-
251-
const factStmt = db.prepare(
252-
"INSERT INTO session_facts (session_id, category, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
253-
);
254-
for (const f of facts) {
255-
factStmt.run(sessionId, f.category, f.content, now, now);
256-
}
260+
insertCompartmentRows(db, sessionId, compartments, now);
261+
insertFactRows(db, sessionId, facts, now);
257262

258263
// Clear cached memory block so next injection renders fresh (historian run already busts cache)
259264
db.prepare(
@@ -401,29 +406,8 @@ export function promoteRecompStaging(
401406
db.prepare("DELETE FROM compartments WHERE session_id = ?").run(sessionId);
402407
db.prepare("DELETE FROM session_facts WHERE session_id = ?").run(sessionId);
403408

404-
const compartmentStmt = db.prepare(
405-
"INSERT INTO compartments (session_id, sequence, start_message, end_message, start_message_id, end_message_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
406-
);
407-
for (const c of staging.compartments) {
408-
compartmentStmt.run(
409-
sessionId,
410-
c.sequence,
411-
c.startMessage,
412-
c.endMessage,
413-
c.startMessageId,
414-
c.endMessageId,
415-
c.title,
416-
c.content,
417-
now,
418-
);
419-
}
420-
421-
const factStmt = db.prepare(
422-
"INSERT INTO session_facts (session_id, category, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
423-
);
424-
for (const f of staging.facts) {
425-
factStmt.run(sessionId, f.category, f.content, now, now);
426-
}
409+
insertCompartmentRows(db, sessionId, staging.compartments, now);
410+
insertFactRows(db, sessionId, staging.facts, now);
427411

428412
// Clear staging
429413
db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);

src/features/magic-context/dreamer/queue.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,3 @@ export function clearStaleEntries(db: Database, maxAgeMs: number): number {
121121
.run(cutoff);
122122
return result.changes;
123123
}
124-
125-
/** Get current queue size (unstarted entries only). */
126-
export function getQueueSize(db: Database): number {
127-
const row = db
128-
.query<{ count: number }, []>(
129-
"SELECT COUNT(*) as count FROM dream_queue WHERE started_at IS NULL",
130-
)
131-
.get();
132-
return row?.count ?? 0;
133-
}

src/features/magic-context/scheduler.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/// <reference types="bun-types" />
2+
13
import { describe, expect, it } from "bun:test";
24
import { createScheduler, parseCacheTtl } from "./scheduler";
35
import type { ContextUsage, SessionMeta } from "./types";
@@ -18,6 +20,7 @@ function createSessionMeta(overrides: Partial<SessionMeta> = {}): SessionMeta {
1820
lastInputTokens: 0,
1921
timesExecuteThresholdReached: 0,
2022
compartmentInProgress: false,
23+
systemPromptHash: "",
2124
...overrides,
2225
};
2326
}
@@ -72,6 +75,23 @@ describe("createScheduler", () => {
7275
);
7376
});
7477

78+
it("uses a model-specific execute threshold when modelKey is provided", () => {
79+
const scheduler = createScheduler({
80+
executeThresholdPercentage: { default: 70, "openai/gpt-4o": 60 },
81+
});
82+
const sessionMeta = createSessionMeta({ lastResponseTime: BASE_TIME - 10_000 });
83+
84+
expect(
85+
scheduler.shouldExecute(
86+
sessionMeta,
87+
createContextUsage(65),
88+
BASE_TIME,
89+
undefined,
90+
"openai/gpt-4o",
91+
),
92+
).toBe("execute");
93+
});
94+
7595
it("falls back to 5m default when cacheTtl is invalid", () => {
7696
const scheduler = createScheduler({ executeThresholdPercentage: 65 });
7797
const sessionMeta = createSessionMeta({

src/features/magic-context/scheduler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface Scheduler {
1717
contextUsage: ContextUsage,
1818
currentTime?: number,
1919
sessionId?: string,
20+
modelKey?: string,
2021
): SchedulerDecision;
2122
}
2223

@@ -48,10 +49,11 @@ export function createScheduler(config: SchedulerConfig): Scheduler {
4849
contextUsage: ContextUsage,
4950
currentTime: number = Date.now(),
5051
sessionId?: string,
52+
modelKey?: string,
5153
): SchedulerDecision {
5254
const threshold = resolveExecuteThreshold(
5355
config.executeThresholdPercentage,
54-
undefined,
56+
modelKey,
5557
65,
5658
);
5759
if (contextUsage.percentage >= threshold) {

0 commit comments

Comments
 (0)