feat: add client-side sleeptime settings + compaction reflection triggers (#923)

This commit is contained in:
Charles Packer
2026-02-11 19:18:22 -08:00
committed by GitHub
parent c3a7f6c646
commit 9ea10bf2ff
12 changed files with 975 additions and 41 deletions

View File

@@ -1,6 +1,7 @@
import { describe, expect, test } from "bun:test";
import type { LettaStreamingResponse } from "@letta-ai/letta-client/resources/agents/messages";
import { createBuffers, onChunk } from "../../cli/helpers/accumulator";
import { createContextTracker } from "../../cli/helpers/contextTracker";
function usageChunk(
fields: Record<string, number | null | undefined>,
@@ -72,4 +73,36 @@ describe("accumulator usage statistics", () => {
expect(buffers.usage.reasoningTokens).toBe(0);
expect(buffers.usage.contextTokens).toBeUndefined();
});
test("sets reflection trigger only after compaction summary message", () => {
const buffers = createBuffers("agent-1");
const tracker = createContextTracker();
onChunk(
buffers,
{
message_type: "event_message",
otid: "evt-compaction-1",
event_type: "compaction",
event_data: {},
},
tracker,
);
expect(tracker.pendingReflectionTrigger).toBe(false);
onChunk(
buffers,
{
message_type: "summary_message",
otid: "evt-compaction-1",
summary: "Compaction completed",
},
tracker,
);
expect(tracker.pendingCompaction).toBe(true);
expect(tracker.pendingSkillsReinject).toBe(true);
expect(tracker.pendingReflectionTrigger).toBe(true);
});
});

View File

@@ -0,0 +1,28 @@
import { describe, expect, test } from "bun:test";
import {
createContextTracker,
resetContextHistory,
} from "../../cli/helpers/contextTracker";
describe("contextTracker", () => {
test("resetContextHistory clears token history and pending compaction flags", () => {
const tracker = createContextTracker();
tracker.lastContextTokens = 123;
tracker.contextTokensHistory = [
{ timestamp: 1, tokens: 111, turnId: 1, compacted: true },
];
tracker.pendingCompaction = true;
tracker.pendingSkillsReinject = true;
tracker.pendingReflectionTrigger = true;
tracker.currentTurnId = 9;
resetContextHistory(tracker);
expect(tracker.lastContextTokens).toBe(0);
expect(tracker.contextTokensHistory).toEqual([]);
expect(tracker.pendingCompaction).toBe(false);
expect(tracker.pendingSkillsReinject).toBe(false);
expect(tracker.pendingReflectionTrigger).toBe(false);
expect(tracker.currentTurnId).toBe(9);
});
});

View File

@@ -0,0 +1,147 @@
import { afterEach, describe, expect, test } from "bun:test";
import {
MEMORY_CHECK_REMINDER,
MEMORY_REFLECTION_REMINDER,
} from "../../agent/promptAssets";
import {
buildCompactionMemoryReminder,
buildMemoryReminder,
getReflectionSettings,
reflectionSettingsToLegacyMode,
} from "../../cli/helpers/memoryReminder";
import { settingsManager } from "../../settings-manager";
const originalGetLocalProjectSettings = settingsManager.getLocalProjectSettings;
const originalGetSettings = settingsManager.getSettings;
const originalIsMemfsEnabled = settingsManager.isMemfsEnabled;
afterEach(() => {
(settingsManager as typeof settingsManager).getLocalProjectSettings =
originalGetLocalProjectSettings;
(settingsManager as typeof settingsManager).getSettings = originalGetSettings;
(settingsManager as typeof settingsManager).isMemfsEnabled =
originalIsMemfsEnabled;
});
describe("memoryReminder", () => {
test("prefers local reflection settings over global", () => {
(settingsManager as typeof settingsManager).getLocalProjectSettings = () =>
({
reflectionTrigger: "compaction-event",
reflectionBehavior: "auto-launch",
reflectionStepCount: 33,
}) as ReturnType<typeof settingsManager.getLocalProjectSettings>;
(settingsManager as typeof settingsManager).getSettings = (() =>
({
memoryReminderInterval: 5,
reflectionTrigger: "step-count",
reflectionBehavior: "reminder",
reflectionStepCount: 25,
}) as ReturnType<
typeof settingsManager.getSettings
>) as typeof settingsManager.getSettings;
expect(getReflectionSettings()).toEqual({
trigger: "compaction-event",
behavior: "auto-launch",
stepCount: 33,
});
});
test("falls back to legacy local mode when split fields are absent", () => {
(settingsManager as typeof settingsManager).getLocalProjectSettings = () =>
({
memoryReminderInterval: "compaction",
}) as ReturnType<typeof settingsManager.getLocalProjectSettings>;
(settingsManager as typeof settingsManager).getSettings = (() =>
({
memoryReminderInterval: 5,
reflectionTrigger: "step-count",
reflectionBehavior: "reminder",
reflectionStepCount: 25,
}) as ReturnType<
typeof settingsManager.getSettings
>) as typeof settingsManager.getSettings;
expect(getReflectionSettings()).toEqual({
trigger: "compaction-event",
behavior: "reminder",
stepCount: 25,
});
});
test("disables turn-based reminders for non-step-count trigger", async () => {
(settingsManager as typeof settingsManager).getLocalProjectSettings = () =>
({
reflectionTrigger: "compaction-event",
reflectionBehavior: "reminder",
}) as ReturnType<typeof settingsManager.getLocalProjectSettings>;
(settingsManager as typeof settingsManager).getSettings = (() =>
({
memoryReminderInterval: 5,
reflectionTrigger: "step-count",
reflectionBehavior: "reminder",
reflectionStepCount: 25,
}) as ReturnType<
typeof settingsManager.getSettings
>) as typeof settingsManager.getSettings;
const reminder = await buildMemoryReminder(10, "agent-1");
expect(reminder).toBe("");
});
test("keeps existing numeric interval behavior", async () => {
(settingsManager as typeof settingsManager).getLocalProjectSettings = () =>
({
reflectionTrigger: "step-count",
reflectionBehavior: "auto-launch",
reflectionStepCount: 5,
}) as ReturnType<typeof settingsManager.getLocalProjectSettings>;
(settingsManager as typeof settingsManager).getSettings = (() =>
({
memoryReminderInterval: 10,
reflectionTrigger: "step-count",
reflectionBehavior: "reminder",
reflectionStepCount: 25,
}) as ReturnType<
typeof settingsManager.getSettings
>) as typeof settingsManager.getSettings;
(settingsManager as typeof settingsManager).isMemfsEnabled = (() =>
false) as typeof settingsManager.isMemfsEnabled;
const reminder = await buildMemoryReminder(10, "agent-1");
expect(reminder).toBe(MEMORY_CHECK_REMINDER);
});
test("maps split reflection settings back to legacy mode", () => {
expect(
reflectionSettingsToLegacyMode({
trigger: "off",
behavior: "reminder",
stepCount: 25,
}),
).toBeNull();
expect(
reflectionSettingsToLegacyMode({
trigger: "step-count",
behavior: "auto-launch",
stepCount: 30,
}),
).toBe(30);
expect(
reflectionSettingsToLegacyMode({
trigger: "compaction-event",
behavior: "auto-launch",
stepCount: 25,
}),
).toBe("auto-compaction");
});
test("builds compaction reminder with memfs-aware reflection content", async () => {
(settingsManager as typeof settingsManager).isMemfsEnabled = (() =>
true) as typeof settingsManager.isMemfsEnabled;
const reminder = await buildCompactionMemoryReminder("agent-1");
expect(reminder).toBe(MEMORY_REFLECTION_REMINDER);
});
});