feat: split init into shallow and deep tiers [LET-7770] (#1258)

This commit is contained in:
Devansh Jain
2026-03-03 22:56:58 -08:00
committed by GitHub
parent 4111c546d3
commit 52e7a5af3a
9 changed files with 316 additions and 11 deletions

View File

@@ -83,7 +83,7 @@ describe("auto-init lifecycle guards", () => {
expect(blockStart).toBeGreaterThan(-1);
// Extract enough of the block to cover the clearing logic
const block = appSource.slice(blockStart, blockStart + 600);
const block = appSource.slice(blockStart, blockStart + 1200);
// The delete must happen AFTER checking `fired`, not before fireAutoInit
const firedCheck = block.indexOf("if (fired)");
@@ -95,17 +95,23 @@ describe("auto-init lifecycle guards", () => {
expect(setDelete).toBeGreaterThan(firedCheck);
});
test("manual /init clears pending auto-init for current agent", () => {
test("manual /init clears pending auto-init for current agent after spawn", () => {
const appSource = readSource("../../cli/App.tsx");
// The /init handler must delete the current agent from the pending set
// The /init handler must delete the current agent from the pending set,
// but only after the background subagent has been spawned (inside the try).
const initHandlerIdx = appSource.indexOf('trimmed === "/init"');
expect(initHandlerIdx).toBeGreaterThan(-1);
const afterInit = appSource.slice(initHandlerIdx, initHandlerIdx + 400);
expect(afterInit).toContain(
// Search from the /init handler to the end of the block
const afterInit = appSource.slice(initHandlerIdx);
const spawnIdx = afterInit.indexOf("spawnBackgroundSubagentTask({");
const deleteIdx = afterInit.indexOf(
"autoInitPendingAgentIdsRef.current.delete(agentId)",
);
expect(spawnIdx).toBeGreaterThan(-1);
expect(deleteIdx).toBeGreaterThan(-1);
expect(deleteIdx).toBeGreaterThan(spawnIdx);
});
test("fireAutoInit returns false (not throw) when init subagent is active", () => {

View File

@@ -1,6 +1,7 @@
import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { buildMemoryInitRuntimePrompt } from "../../cli/helpers/initCommand";
describe("init background subagent wiring", () => {
const readSource = (relativePath: string) =>
@@ -70,4 +71,44 @@ describe("init background subagent wiring", () => {
);
expect(indexSource).toContain("initAgentMd");
});
const baseArgs = {
agentId: "test-agent",
workingDirectory: "/tmp/test",
memoryDir: "/tmp/test/.memory",
gitContext: "## Git context\nsome git info",
};
test('buildMemoryInitRuntimePrompt includes "research_depth: shallow" when depth is "shallow"', () => {
const prompt = buildMemoryInitRuntimePrompt({
...baseArgs,
depth: "shallow",
});
expect(prompt).toContain("research_depth: shallow");
expect(prompt).toContain("Shallow init");
expect(prompt).not.toContain("Deep init");
});
test('buildMemoryInitRuntimePrompt includes "research_depth: deep" when depth is "deep"', () => {
const prompt = buildMemoryInitRuntimePrompt({
...baseArgs,
depth: "deep",
});
expect(prompt).toContain("research_depth: deep");
expect(prompt).toContain("Deep init");
expect(prompt).not.toContain("Shallow init");
});
test('buildMemoryInitRuntimePrompt defaults to "deep" when depth is omitted', () => {
const prompt = buildMemoryInitRuntimePrompt(baseArgs);
expect(prompt).toContain("research_depth: deep");
expect(prompt).toContain("Deep init");
});
test("App.tsx contains maybeLaunchDeepInitSubagent", () => {
const appSource = readSource("../../cli/App.tsx");
expect(appSource).toContain("maybeLaunchDeepInitSubagent");
expect(appSource).toContain("Deep memory initialization");
expect(appSource).toContain('depth: "deep"');
});
});

View File

@@ -10,6 +10,11 @@ import {
reflectionSettingsToLegacyMode,
shouldFireStepCountTrigger,
} from "../../cli/helpers/memoryReminder";
import {
type SharedReminderContext,
sharedReminderProviders,
} from "../../reminders/engine";
import { createSharedReminderState } from "../../reminders/state";
import { settingsManager } from "../../settings-manager";
const originalGetLocalProjectSettings = settingsManager.getLocalProjectSettings;
@@ -170,3 +175,121 @@ describe("memoryReminder", () => {
).toBe(false);
});
});
describe("deep-init trigger", () => {
const deepInitProvider = sharedReminderProviders["deep-init"];
function makeContext(
overrides: Partial<{
shallowInitCompleted: boolean;
deepInitFired: boolean;
turnCount: number;
memfsEnabled: boolean;
callback: (() => Promise<boolean>) | undefined;
}> = {},
): SharedReminderContext {
const state = createSharedReminderState();
state.shallowInitCompleted = overrides.shallowInitCompleted ?? false;
state.deepInitFired = overrides.deepInitFired ?? false;
state.turnCount = overrides.turnCount ?? 0;
const memfsEnabled = overrides.memfsEnabled ?? true;
(settingsManager as typeof settingsManager).isMemfsEnabled = (() =>
memfsEnabled) as typeof settingsManager.isMemfsEnabled;
return {
mode: "interactive",
agent: { id: "test-agent", name: "test" },
state,
sessionContextReminderEnabled: false,
reflectionSettings: {
trigger: "step-count",
behavior: "auto-launch",
stepCount: 25,
},
skillSources: [],
resolvePlanModeReminder: async () => "",
maybeLaunchDeepInitSubagent: overrides.callback,
};
}
test("does not fire before turn 8", async () => {
let launched = false;
const ctx = makeContext({
shallowInitCompleted: true,
turnCount: 7,
callback: async () => {
launched = true;
return true;
},
});
const result = await deepInitProvider(ctx);
expect(result).toBeNull();
expect(launched).toBe(false);
});
// Deep init auto-launch is currently disabled (reflection + deep init
// at similar turn counts is too chaotic). This test documents the
// disabled behavior; re-enable when subagent prompts are tuned.
test("is currently disabled — does not launch even when conditions are met", async () => {
let launched = false;
const ctx = makeContext({
shallowInitCompleted: true,
turnCount: 8,
callback: async () => {
launched = true;
return true;
},
});
const result = await deepInitProvider(ctx);
expect(result).toBeNull();
expect(launched).toBe(false);
});
test("does not re-fire once deepInitFired is true", async () => {
let launched = false;
const ctx = makeContext({
shallowInitCompleted: true,
deepInitFired: true,
turnCount: 10,
callback: async () => {
launched = true;
return true;
},
});
const result = await deepInitProvider(ctx);
expect(result).toBeNull();
expect(launched).toBe(false);
});
test("does not fire when shallowInitCompleted is false", async () => {
let launched = false;
const ctx = makeContext({
shallowInitCompleted: false,
turnCount: 10,
callback: async () => {
launched = true;
return true;
},
});
const result = await deepInitProvider(ctx);
expect(result).toBeNull();
expect(launched).toBe(false);
});
test("does not fire when memfs is disabled", async () => {
let launched = false;
const ctx = makeContext({
shallowInitCompleted: true,
turnCount: 8,
memfsEnabled: false,
callback: async () => {
launched = true;
return true;
},
});
const result = await deepInitProvider(ctx);
expect(result).toBeNull();
expect(launched).toBe(false);
});
});

View File

@@ -144,12 +144,14 @@ describe("background onComplete → flush wiring in App.tsx", () => {
expect(onCompleteIdx).toBeGreaterThan(-1);
// The appendTaskNotificationEvents call must be within the onComplete body
// (before the next closing of spawnBackgroundSubagentTask)
// (before the closing of spawnBackgroundSubagentTask).
// Use "});" with leading whitespace to match the spawn call's closing,
// not inner statements like updateInitProgress(...});
const callIdx = source.indexOf(
"appendTaskNotificationEvents(",
onCompleteIdx,
);
const blockEnd = source.indexOf("});", onCompleteIdx);
const blockEnd = source.indexOf(" });", onCompleteIdx);
expect(callIdx).toBeGreaterThan(onCompleteIdx);
expect(callIdx).toBeLessThan(blockEnd);
});