fix: close memory index and refresh protocol outputs
This commit is contained in:
@@ -1352,6 +1352,7 @@ public struct SkillsUpdateParams: Codable, Sendable {
|
|||||||
|
|
||||||
public struct CronJob: Codable, Sendable {
|
public struct CronJob: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
|
public let agentid: String?
|
||||||
public let name: String
|
public let name: String
|
||||||
public let description: String?
|
public let description: String?
|
||||||
public let enabled: Bool
|
public let enabled: Bool
|
||||||
@@ -1366,6 +1367,7 @@ public struct CronJob: Codable, Sendable {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: String,
|
id: String,
|
||||||
|
agentid: String?,
|
||||||
name: String,
|
name: String,
|
||||||
description: String?,
|
description: String?,
|
||||||
enabled: Bool,
|
enabled: Bool,
|
||||||
@@ -1379,6 +1381,7 @@ public struct CronJob: Codable, Sendable {
|
|||||||
state: [String: AnyCodable]
|
state: [String: AnyCodable]
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
|
self.agentid = agentid
|
||||||
self.name = name
|
self.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
@@ -1393,6 +1396,7 @@ public struct CronJob: Codable, Sendable {
|
|||||||
}
|
}
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case id
|
case id
|
||||||
|
case agentid = "agentId"
|
||||||
case name
|
case name
|
||||||
case description
|
case description
|
||||||
case enabled
|
case enabled
|
||||||
@@ -1425,6 +1429,7 @@ public struct CronStatusParams: Codable, Sendable {
|
|||||||
|
|
||||||
public struct CronAddParams: Codable, Sendable {
|
public struct CronAddParams: Codable, Sendable {
|
||||||
public let name: String
|
public let name: String
|
||||||
|
public let agentid: AnyCodable?
|
||||||
public let description: String?
|
public let description: String?
|
||||||
public let enabled: Bool?
|
public let enabled: Bool?
|
||||||
public let schedule: AnyCodable
|
public let schedule: AnyCodable
|
||||||
@@ -1435,6 +1440,7 @@ public struct CronAddParams: Codable, Sendable {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
name: String,
|
name: String,
|
||||||
|
agentid: AnyCodable?,
|
||||||
description: String?,
|
description: String?,
|
||||||
enabled: Bool?,
|
enabled: Bool?,
|
||||||
schedule: AnyCodable,
|
schedule: AnyCodable,
|
||||||
@@ -1444,6 +1450,7 @@ public struct CronAddParams: Codable, Sendable {
|
|||||||
isolation: [String: AnyCodable]?
|
isolation: [String: AnyCodable]?
|
||||||
) {
|
) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.agentid = agentid
|
||||||
self.description = description
|
self.description = description
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
self.schedule = schedule
|
self.schedule = schedule
|
||||||
@@ -1454,6 +1461,7 @@ public struct CronAddParams: Codable, Sendable {
|
|||||||
}
|
}
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case name
|
case name
|
||||||
|
case agentid = "agentId"
|
||||||
case description
|
case description
|
||||||
case enabled
|
case enabled
|
||||||
case schedule
|
case schedule
|
||||||
|
|||||||
@@ -1158,16 +1158,16 @@ export async function handleDirectiveOnly(params: {
|
|||||||
await saveSessionStore(storePath, sessionStore);
|
await saveSessionStore(storePath, sessionStore);
|
||||||
}
|
}
|
||||||
if (elevatedChanged) {
|
if (elevatedChanged) {
|
||||||
const nextElevated =
|
const nextElevated = (sessionEntry.elevatedLevel ??
|
||||||
(sessionEntry.elevatedLevel ?? "off") as ElevatedLevel;
|
"off") as ElevatedLevel;
|
||||||
enqueueSystemEvent(formatElevatedEvent(nextElevated), {
|
enqueueSystemEvent(formatElevatedEvent(nextElevated), {
|
||||||
sessionKey,
|
sessionKey,
|
||||||
contextKey: "mode:elevated",
|
contextKey: "mode:elevated",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (reasoningChanged) {
|
if (reasoningChanged) {
|
||||||
const nextReasoning =
|
const nextReasoning = (sessionEntry.reasoningLevel ??
|
||||||
(sessionEntry.reasoningLevel ?? "off") as ReasoningLevel;
|
"off") as ReasoningLevel;
|
||||||
enqueueSystemEvent(formatReasoningEvent(nextReasoning), {
|
enqueueSystemEvent(formatReasoningEvent(nextReasoning), {
|
||||||
sessionKey,
|
sessionKey,
|
||||||
contextKey: "mode:reasoning",
|
contextKey: "mode:reasoning",
|
||||||
@@ -1414,16 +1414,16 @@ export async function persistInlineDirectives(params: {
|
|||||||
await saveSessionStore(storePath, sessionStore);
|
await saveSessionStore(storePath, sessionStore);
|
||||||
}
|
}
|
||||||
if (elevatedChanged) {
|
if (elevatedChanged) {
|
||||||
const nextElevated =
|
const nextElevated = (sessionEntry.elevatedLevel ??
|
||||||
(sessionEntry.elevatedLevel ?? "off") as ElevatedLevel;
|
"off") as ElevatedLevel;
|
||||||
enqueueSystemEvent(formatElevatedEvent(nextElevated), {
|
enqueueSystemEvent(formatElevatedEvent(nextElevated), {
|
||||||
sessionKey,
|
sessionKey,
|
||||||
contextKey: "mode:elevated",
|
contextKey: "mode:elevated",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (reasoningChanged) {
|
if (reasoningChanged) {
|
||||||
const nextReasoning =
|
const nextReasoning = (sessionEntry.reasoningLevel ??
|
||||||
(sessionEntry.reasoningLevel ?? "off") as ReasoningLevel;
|
"off") as ReasoningLevel;
|
||||||
enqueueSystemEvent(formatReasoningEvent(nextReasoning), {
|
enqueueSystemEvent(formatReasoningEvent(nextReasoning), {
|
||||||
sessionKey,
|
sessionKey,
|
||||||
contextKey: "mode:reasoning",
|
contextKey: "mode:reasoning",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import path from "node:path";
|
|||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { getMemorySearchManager } from "./index.js";
|
import { getMemorySearchManager, type MemoryIndexManager } from "./index.js";
|
||||||
|
|
||||||
vi.mock("./embeddings.js", () => {
|
vi.mock("./embeddings.js", () => {
|
||||||
const embedText = (text: string) => {
|
const embedText = (text: string) => {
|
||||||
@@ -29,6 +29,7 @@ vi.mock("./embeddings.js", () => {
|
|||||||
describe("memory index", () => {
|
describe("memory index", () => {
|
||||||
let workspaceDir: string;
|
let workspaceDir: string;
|
||||||
let indexPath: string;
|
let indexPath: string;
|
||||||
|
let manager: MemoryIndexManager | null = null;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-mem-"));
|
workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-mem-"));
|
||||||
@@ -45,6 +46,10 @@ describe("memory index", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
if (manager) {
|
||||||
|
await manager.close();
|
||||||
|
manager = null;
|
||||||
|
}
|
||||||
await fs.rm(workspaceDir, { recursive: true, force: true });
|
await fs.rm(workspaceDir, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -67,6 +72,7 @@ describe("memory index", () => {
|
|||||||
const result = await getMemorySearchManager({ cfg, agentId: "main" });
|
const result = await getMemorySearchManager({ cfg, agentId: "main" });
|
||||||
expect(result.manager).not.toBeNull();
|
expect(result.manager).not.toBeNull();
|
||||||
if (!result.manager) throw new Error("manager missing");
|
if (!result.manager) throw new Error("manager missing");
|
||||||
|
manager = result.manager;
|
||||||
await result.manager.sync({ force: true });
|
await result.manager.sync({ force: true });
|
||||||
const results = await result.manager.search("alpha");
|
const results = await result.manager.search("alpha");
|
||||||
expect(results.length).toBeGreaterThan(0);
|
expect(results.length).toBeGreaterThan(0);
|
||||||
@@ -91,6 +97,7 @@ describe("memory index", () => {
|
|||||||
const result = await getMemorySearchManager({ cfg, agentId: "main" });
|
const result = await getMemorySearchManager({ cfg, agentId: "main" });
|
||||||
expect(result.manager).not.toBeNull();
|
expect(result.manager).not.toBeNull();
|
||||||
if (!result.manager) throw new Error("manager missing");
|
if (!result.manager) throw new Error("manager missing");
|
||||||
|
manager = result.manager;
|
||||||
await expect(
|
await expect(
|
||||||
result.manager.readFile({ relPath: "NOTES.md" }),
|
result.manager.readFile({ relPath: "NOTES.md" }),
|
||||||
).rejects.toThrow("path required");
|
).rejects.toThrow("path required");
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const SNIPPET_MAX_CHARS = 700;
|
|||||||
const INDEX_CACHE = new Map<string, MemoryIndexManager>();
|
const INDEX_CACHE = new Map<string, MemoryIndexManager>();
|
||||||
|
|
||||||
export class MemoryIndexManager {
|
export class MemoryIndexManager {
|
||||||
|
private readonly cacheKey: string;
|
||||||
private readonly cfg: ClawdbotConfig;
|
private readonly cfg: ClawdbotConfig;
|
||||||
private readonly agentId: string;
|
private readonly agentId: string;
|
||||||
private readonly workspaceDir: string;
|
private readonly workspaceDir: string;
|
||||||
@@ -67,6 +68,7 @@ export class MemoryIndexManager {
|
|||||||
private watcher: FSWatcher | null = null;
|
private watcher: FSWatcher | null = null;
|
||||||
private watchTimer: NodeJS.Timeout | null = null;
|
private watchTimer: NodeJS.Timeout | null = null;
|
||||||
private intervalTimer: NodeJS.Timeout | null = null;
|
private intervalTimer: NodeJS.Timeout | null = null;
|
||||||
|
private closed = false;
|
||||||
private dirty = false;
|
private dirty = false;
|
||||||
private sessionWarm = new Set<string>();
|
private sessionWarm = new Set<string>();
|
||||||
private syncing: Promise<void> | null = null;
|
private syncing: Promise<void> | null = null;
|
||||||
@@ -91,6 +93,7 @@ export class MemoryIndexManager {
|
|||||||
local: settings.local,
|
local: settings.local,
|
||||||
});
|
});
|
||||||
const manager = new MemoryIndexManager({
|
const manager = new MemoryIndexManager({
|
||||||
|
cacheKey: key,
|
||||||
cfg,
|
cfg,
|
||||||
agentId,
|
agentId,
|
||||||
workspaceDir,
|
workspaceDir,
|
||||||
@@ -102,12 +105,14 @@ export class MemoryIndexManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private constructor(params: {
|
private constructor(params: {
|
||||||
|
cacheKey: string;
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
agentId: string;
|
agentId: string;
|
||||||
workspaceDir: string;
|
workspaceDir: string;
|
||||||
settings: ResolvedMemorySearchConfig;
|
settings: ResolvedMemorySearchConfig;
|
||||||
providerResult: EmbeddingProviderResult;
|
providerResult: EmbeddingProviderResult;
|
||||||
}) {
|
}) {
|
||||||
|
this.cacheKey = params.cacheKey;
|
||||||
this.cfg = params.cfg;
|
this.cfg = params.cfg;
|
||||||
this.agentId = params.agentId;
|
this.agentId = params.agentId;
|
||||||
this.workspaceDir = params.workspaceDir;
|
this.workspaceDir = params.workspaceDir;
|
||||||
@@ -234,6 +239,25 @@ export class MemoryIndexManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
if (this.closed) return;
|
||||||
|
this.closed = true;
|
||||||
|
if (this.watchTimer) {
|
||||||
|
clearTimeout(this.watchTimer);
|
||||||
|
this.watchTimer = null;
|
||||||
|
}
|
||||||
|
if (this.intervalTimer) {
|
||||||
|
clearInterval(this.intervalTimer);
|
||||||
|
this.intervalTimer = null;
|
||||||
|
}
|
||||||
|
if (this.watcher) {
|
||||||
|
await this.watcher.close();
|
||||||
|
this.watcher = null;
|
||||||
|
}
|
||||||
|
this.db.close();
|
||||||
|
INDEX_CACHE.delete(this.cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
private openDatabase(): DatabaseSync {
|
private openDatabase(): DatabaseSync {
|
||||||
const dbPath = resolveUserPath(this.settings.store.path);
|
const dbPath = resolveUserPath(this.settings.store.path);
|
||||||
const dir = path.dirname(dbPath);
|
const dir = path.dirname(dbPath);
|
||||||
|
|||||||
Reference in New Issue
Block a user