feat(agent): add credential refresh and unify learning tools
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import {
|
||||
executeMemoryManager,
|
||||
executeSkillManager,
|
||||
} from "../../src/learning/toolManagers.js";
|
||||
import { MemoryStore } from "../../src/memory/store.js";
|
||||
import {
|
||||
getRuntimeSessionContext,
|
||||
removeRuntimeSessionContext,
|
||||
setRuntimeSessionContext,
|
||||
type RuntimeSessionContext,
|
||||
} from "../../src/runtime/sessionContext.js";
|
||||
import { SkillStore } from "../../src/skills/store.js";
|
||||
|
||||
describe("main-process learning tool managers", () => {
|
||||
let tempDir: string;
|
||||
let memoryStore: MemoryStore;
|
||||
let skillStore: SkillStore;
|
||||
let context: RuntimeSessionContext;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = await mkdtemp(join(tmpdir(), "tjwater-learning-tools-"));
|
||||
memoryStore = new MemoryStore(
|
||||
join(tempDir, "memory"),
|
||||
join(tempDir, "backup", "memory"),
|
||||
);
|
||||
skillStore = new SkillStore(
|
||||
join(tempDir, "skills"),
|
||||
join(tempDir, "backup", "skills"),
|
||||
);
|
||||
await memoryStore.initialize();
|
||||
context = {
|
||||
actorKey: "actor-1",
|
||||
allowLearningWrite: true,
|
||||
clientSessionId: "client-session-1",
|
||||
projectKey: "project-1",
|
||||
sessionId: "session-1",
|
||||
traceId: "trace-1",
|
||||
};
|
||||
setRuntimeSessionContext(context);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
removeRuntimeSessionContext(context.sessionId);
|
||||
await rm(tempDir, { force: true, recursive: true });
|
||||
});
|
||||
|
||||
it("enforces list-before-add using the canonical runtime context", async () => {
|
||||
const rejected = await executeMemoryManager(memoryStore, context, {
|
||||
action: "add",
|
||||
content: "用户偏好查看压力单位为 MPa",
|
||||
scope: "user",
|
||||
});
|
||||
expect(rejected.decision).toBe("rejected");
|
||||
|
||||
await executeMemoryManager(memoryStore, context, {
|
||||
action: "list",
|
||||
scope: "user",
|
||||
});
|
||||
const refreshedContext = getRuntimeSessionContext(context.sessionId)!;
|
||||
const accepted = await executeMemoryManager(memoryStore, refreshedContext, {
|
||||
action: "add",
|
||||
content: "用户偏好查看压力单位为 MPa",
|
||||
scope: "user",
|
||||
});
|
||||
expect(accepted.decision).toBe("accepted");
|
||||
expect(await memoryStore.list("user", context.actorKey)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("writes and removes skills through the shared store", async () => {
|
||||
const content = [
|
||||
"---",
|
||||
"name: pressure-review",
|
||||
"description: Pressure review workflow.",
|
||||
"---",
|
||||
"",
|
||||
"# Pressure Review",
|
||||
].join("\n");
|
||||
const written = await executeSkillManager(skillStore, context, {
|
||||
action: "write_skill",
|
||||
content,
|
||||
skill_path: "workflow/pressure-review",
|
||||
});
|
||||
expect(written.decision).toBe("accepted");
|
||||
expect("target" in written).toBe(true);
|
||||
if (!("target" in written)) throw new Error("write returned no target");
|
||||
await expect(readFile(written.target, "utf8")).resolves.toContain(
|
||||
"# Pressure Review\n",
|
||||
);
|
||||
|
||||
const removed = await executeSkillManager(skillStore, context, {
|
||||
action: "remove_skill",
|
||||
skill_path: "workflow/pressure-review",
|
||||
});
|
||||
expect(removed.decision).toBe("accepted");
|
||||
expect("target" in removed).toBe(true);
|
||||
if (!("target" in removed)) throw new Error("remove returned no target");
|
||||
await expect(readFile(removed.target, "utf8")).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user