Files
oh-my-pi/packages/coding-agent/test/memory-backend-resolve.test.ts
T
metaphorics 9662844853 feat(coding-agent): support the local memory backend in the learn tool
The `learn` tool previously required a `hindsight`/`mnemopi` backend. It now
also works when `memory.backend` is `local` (the file-based rollout backend):
lessons append to a `learned.md` under the project's memory root, kept separate
from the consolidation artifacts so a consolidation pass never clobbers them,
and are injected into future sessions alongside the memory summary.

- memories: `saveLearnedLesson` (newest-first, deduped, count- and per-field
  size-capped, secret-redacted, injection-neutralized) with per-path write
  serialization; `buildMemoryToolDeveloperInstructions` reads `learned.md` and
  shares one injection budget with the summary; `redactSecrets` extended with
  GitHub/npm/Slack/Google token prefixes.
- local backend: implements `save()`; status reports `writable: true`.
- learn tool: `local` execute branch; `createIf`/`isToolAllowed`/auto-include
  and the standing guidance extended to `local`; local saves tier as a `write`
  approval.
- read-path prompt: renders the learned-lessons block when present.
- Lessons are injection-neutralized and secret-redacted on BOTH write and read
  (they render unescaped into the system prompt).

Also moves the auto-learn CHANGELOG entry out of the released [15.12.6] section
(a cherry-pick artifact) back under [Unreleased] and notes the local backend.

Tests: local storage (format, dedup, cap, redaction incl. provider/delimiter-
split tokens, concurrency), read-back (with/without summary, off-gating, raw
hand-edited file), tool gating + write-approval tiering.
2026-06-14 12:03:13 +09:00

52 lines
1.7 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
import { createMemoryRuntimeContext, resolveMemoryBackend } from "@oh-my-pi/pi-coding-agent/memory-backend";
describe("resolveMemoryBackend", () => {
beforeEach(() => {
resetSettingsForTest();
});
afterEach(() => {
resetSettingsForTest();
});
it("returns the hindsight backend when memory.backend is hindsight, regardless of legacy memories.enabled", async () => {
const a = Settings.isolated({ "memory.backend": "hindsight", "memories.enabled": false });
const b = Settings.isolated({ "memory.backend": "hindsight", "memories.enabled": true });
expect((await resolveMemoryBackend(a)).id).toBe("hindsight");
expect((await resolveMemoryBackend(b)).id).toBe("hindsight");
});
it("exposes inactive status when no session is available", async () => {
const memory = createMemoryRuntimeContext({ agentDir: "/tmp/agent", cwd: "/tmp/project" });
await expect(memory.status()).resolves.toMatchObject({
backend: "off",
active: false,
writable: false,
searchable: false,
});
});
it("reports local backend runtime status as writable (lessons) without structured search", async () => {
const settings = Settings.isolated({ "memory.backend": "local" });
const memory = createMemoryRuntimeContext({
agentDir: "/tmp/agent",
cwd: "/tmp/project",
session: { settings } as never,
});
await expect(memory.status()).resolves.toMatchObject({
backend: "local",
active: true,
writable: true,
searchable: false,
});
await expect(memory.search("project preference")).resolves.toMatchObject({
backend: "local",
count: 0,
});
});
});