From 3db3ace0e9ba4c06f38f1c62e34a4e89c9faa9fb Mon Sep 17 00:00:00 2001 From: engineer Date: Mon, 30 Mar 2026 21:29:54 -0700 Subject: [PATCH] fix(memory): auto-truncate runtime artifacts (#115) --- packages/opencode/src/diagnostic/memory.ts | 6 ++--- packages/opencode/src/util/log.ts | 4 ++-- .../opencode/test/memory/retention.test.ts | 24 +++++++++++++++++++ packages/opencode/test/util/log.test.ts | 4 ++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/opencode/src/diagnostic/memory.ts b/packages/opencode/src/diagnostic/memory.ts index 46cb9adba09d..8e7810fe8681 100644 --- a/packages/opencode/src/diagnostic/memory.ts +++ b/packages/opencode/src/diagnostic/memory.ts @@ -302,13 +302,13 @@ export namespace Memory { export function start(label: string) { if (one.timer) return - const cfg = env() - if (!cfg) return - void Memory.trim().catch((error) => { log.error("memory trim failed", { error }) }) + const cfg = env() + if (!cfg) return + one.file = cfg.path || path.join(Global.Path.log, `memory-${label}-${new Date().toISOString().split(".")[0].replace(/:/g, "")}.ndjson`) diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts index 7d6ed9bf0158..6d9eeb1755b4 100644 --- a/packages/opencode/src/util/log.ts +++ b/packages/opencode/src/util/log.ts @@ -52,7 +52,7 @@ export namespace Log { export function file() { return logpath } - const max = 512 * 1024 * 1024 + export const MAX = 128 * 1024 * 1024 const step = 1024 * 1024 const wait = 5_000 let size = 0 @@ -113,7 +113,7 @@ export namespace Log { file?: string }) { const dir = input?.dir ?? Global.Path.log - const keep = input?.max ?? max + const keep = input?.max ?? MAX const file = input?.file ?? logpath const rows = (await entries(dir)).sort((a, b) => a.time - b.time) let total = rows.reduce((sum, row) => sum + row.size, 0) diff --git a/packages/opencode/test/memory/retention.test.ts b/packages/opencode/test/memory/retention.test.ts index b1fca77688fc..9b61c38bbf54 100644 --- a/packages/opencode/test/memory/retention.test.ts +++ b/packages/opencode/test/memory/retention.test.ts @@ -29,4 +29,28 @@ describe("memory retention", () => { expect(await fs.stat(one).catch(() => undefined)).toBeUndefined() expect((await fs.readdir(tmp.path)).sort()).toEqual(["three", "two"]) }) + + test("defaults to keeping two snapshot directories", async () => { + await using tmp = await tmpdir() + const one = path.join(tmp.path, "one") + const two = path.join(tmp.path, "two") + const three = path.join(tmp.path, "three") + + await fs.mkdir(one, { recursive: true }) + await fs.mkdir(two, { recursive: true }) + await fs.mkdir(three, { recursive: true }) + await fs.writeFile(path.join(one, "meta.json"), "{}") + await fs.writeFile(path.join(two, "meta.json"), "{}") + await fs.writeFile(path.join(three, "meta.json"), "{}") + await fs.utimes(one, new Date("2026-03-01T00:00:00Z"), new Date("2026-03-01T00:00:00Z")) + await fs.utimes(two, new Date("2026-03-02T00:00:00Z"), new Date("2026-03-02T00:00:00Z")) + await fs.utimes(three, new Date("2026-03-03T00:00:00Z"), new Date("2026-03-03T00:00:00Z")) + + await Memory.trim({ + dir: tmp.path, + }) + + expect(await fs.stat(one).catch(() => undefined)).toBeUndefined() + expect((await fs.readdir(tmp.path)).sort()).toEqual(["three", "two"]) + }) }) diff --git a/packages/opencode/test/util/log.test.ts b/packages/opencode/test/util/log.test.ts index 57a5681c1077..edecc4bc3ae8 100644 --- a/packages/opencode/test/util/log.test.ts +++ b/packages/opencode/test/util/log.test.ts @@ -5,6 +5,10 @@ import { Log } from "../../src/util/log" import { tmpdir } from "../fixture/fixture" describe("util.log", () => { + test("defaults log cap to 128MB", () => { + expect(Log.MAX).toBe(128 * 1024 * 1024) + }) + test("deletes old logs and trims the active log to stay within the cap", async () => { await using tmp = await tmpdir() const old = path.join(tmp.path, "old.log")