fix(coding-agent): treat startup metadata as a non-conversation new session
The plan.defaultOnStartup gate keyed off getEntries().length === 0, but createAgentSession appends startup metadata (model_change / thinking_level_change / service_tier_change) for a brand-new session before InteractiveMode.init() runs. So a genuinely new session reached the guard with entries already present and never entered plan mode (the unit harness bypassed the SDK path, hiding it). Gate on the absence of conversation messages (entry.type === "message") instead. A fresh session — including a --continue/auto-resume that created one — carries only metadata and still honors the default; a session with restored conversation keeps its reconciled mode. Added a regression test seeding model/thinking-level metadata.
This commit is contained in:
@@ -116,11 +116,11 @@ describe("InteractiveMode plan.defaultOnStartup", () => {
|
||||
expect(session?.getPlanModeState()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not enter plan mode when the session has restored history", async () => {
|
||||
// A genuinely resumed session has prior entries; gating on getEntries()
|
||||
// (not the CLI resume flag) means a `--continue` that created a *fresh*,
|
||||
// empty session still gets the startup default (the "enters" case above),
|
||||
// while one with restored history is left in its reconciled mode.
|
||||
it("does not enter plan mode when the session has restored conversation", async () => {
|
||||
// A genuinely resumed session has prior conversation messages. Gating on
|
||||
// message entries (not the CLI resume flag) means a `--continue` that
|
||||
// created a *fresh* session still gets the startup default (above), while
|
||||
// one with restored conversation is left in its reconciled mode.
|
||||
const created = createHarness(Settings.isolated({ "plan.defaultOnStartup": true, "compaction.enabled": false }));
|
||||
created.sessionManager.appendMessage({ role: "user", content: "prior turn", timestamp: Date.now() });
|
||||
|
||||
@@ -130,6 +130,21 @@ describe("InteractiveMode plan.defaultOnStartup", () => {
|
||||
expect(session?.getPlanModeState()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("enters plan mode for a fresh session that carries only startup metadata", async () => {
|
||||
// createAgentSession appends model_change / thinking_level_change for a
|
||||
// brand-new session before init(); those are not conversation history, so
|
||||
// the startup default must still apply (regression: gating on entry count
|
||||
// instead of message entries skipped plan mode for every real new session).
|
||||
const created = createHarness(Settings.isolated({ "plan.defaultOnStartup": true, "compaction.enabled": false }));
|
||||
created.sessionManager.appendModelChange("anthropic/claude-sonnet-4-5");
|
||||
created.sessionManager.appendThinkingLevelChange("medium");
|
||||
|
||||
await created.init({ suppressWelcomeIntro: true });
|
||||
|
||||
expect(created.planModeEnabled).toBe(true);
|
||||
expect(session?.getPlanModeState()).toMatchObject({ enabled: true });
|
||||
});
|
||||
|
||||
it("does not enter plan mode when plan mode is globally disabled", async () => {
|
||||
const created = createHarness(
|
||||
Settings.isolated({ "plan.defaultOnStartup": true, "plan.enabled": false, "compaction.enabled": false }),
|
||||
|
||||
Reference in New Issue
Block a user