fix(session): prevent goal mode pause during compaction/switch
This commit is contained in:
@@ -80,6 +80,9 @@ function createHarness(initial: { state?: GoalModeState; usage?: GoalTokenUsage;
|
||||
return {
|
||||
runtime: new GoalRuntime(host),
|
||||
getState: () => cloneState(state),
|
||||
setState: (next: GoalModeState | undefined) => {
|
||||
state = cloneState(next);
|
||||
},
|
||||
setUsage: (next: Partial<GoalTokenUsage>) => {
|
||||
usage = createUsage(next);
|
||||
},
|
||||
@@ -153,6 +156,65 @@ describe("goal runtime", () => {
|
||||
expect(harness.persists).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("persists wall-clock-only usage before internal compaction or session-switch aborts", async () => {
|
||||
const harness = createHarness({
|
||||
state: { enabled: true, mode: "active", goal: createGoal() },
|
||||
});
|
||||
|
||||
harness.runtime.onTurnStart("turn-1", createUsage());
|
||||
harness.advance(2_500);
|
||||
await harness.runtime.onTaskAborted({ reason: "internal" });
|
||||
|
||||
expect(harness.getState()?.enabled).toBe(true);
|
||||
expect(harness.getState()?.goal.status).toBe("active");
|
||||
expect(harness.getState()?.goal.timeUsedSeconds).toBe(2);
|
||||
expect(harness.persists).toHaveLength(1);
|
||||
expect(harness.persists[0]).toMatchObject({
|
||||
mode: "goal",
|
||||
state: { goal: { timeUsedSeconds: 2 } },
|
||||
});
|
||||
});
|
||||
|
||||
it("resets wall-clock baseline when preserving an active goal after a no-goal switch", async () => {
|
||||
const goal = createGoal();
|
||||
const harness = createHarness({
|
||||
state: { enabled: true, mode: "active", goal },
|
||||
});
|
||||
|
||||
harness.runtime.onTurnStart("turn-1", createUsage());
|
||||
harness.setState(undefined);
|
||||
harness.advance(10_000);
|
||||
harness.setState({ enabled: true, mode: "active", goal });
|
||||
|
||||
const resumed = await harness.runtime.onThreadResumed({ preserveActiveGoal: true });
|
||||
harness.advance(1_000);
|
||||
await harness.runtime.flushUsage("suppressed");
|
||||
|
||||
expect(resumed?.goal.status).toBe("active");
|
||||
expect(harness.getState()?.goal.timeUsedSeconds).toBe(1);
|
||||
expect(harness.runtime.snapshot.wallClock.lastAccountedAt).toBe(11_000);
|
||||
});
|
||||
|
||||
it("clears stale accounting when reconciling to a no-goal session", async () => {
|
||||
const goal = createGoal();
|
||||
const harness = createHarness({
|
||||
state: { enabled: true, mode: "active", goal },
|
||||
});
|
||||
|
||||
harness.runtime.onTurnStart("turn-1", createUsage());
|
||||
harness.setState(undefined);
|
||||
harness.runtime.clearAccounting();
|
||||
harness.advance(10_000);
|
||||
harness.setState({ enabled: true, mode: "active", goal });
|
||||
|
||||
await harness.runtime.onThreadResumed({ preserveActiveGoal: true });
|
||||
harness.advance(1_000);
|
||||
await harness.runtime.flushUsage("suppressed");
|
||||
|
||||
expect(harness.getState()?.goal.timeUsedSeconds).toBe(1);
|
||||
expect(harness.runtime.snapshot.wallClock.lastAccountedAt).toBe(11_000);
|
||||
});
|
||||
|
||||
it("steers only once until a budget mutation resets the cycle", async () => {
|
||||
const harness = createHarness({
|
||||
state: {
|
||||
@@ -219,6 +281,20 @@ describe("goal runtime", () => {
|
||||
expect(harness.persists.at(-1)?.mode).toBe("goal_paused");
|
||||
});
|
||||
|
||||
it("preserves an active goal during internal session-switch reconciliation", async () => {
|
||||
const harness = createHarness({
|
||||
state: { enabled: true, mode: "active", goal: createGoal() },
|
||||
});
|
||||
|
||||
const resumed = await harness.runtime.onThreadResumed({ preserveActiveGoal: true });
|
||||
|
||||
expect(resumed?.enabled).toBe(true);
|
||||
expect(resumed?.goal.status).toBe("active");
|
||||
expect(harness.getState()?.enabled).toBe(true);
|
||||
expect(harness.getState()?.goal.status).toBe("active");
|
||||
expect(harness.persists).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("escapes XML in goal helpers and rendered prompts", () => {
|
||||
const objective = "Fix <root>&keep>safe";
|
||||
const goal = createGoal({ objective });
|
||||
|
||||
Reference in New Issue
Block a user