feat: split init into shallow and deep tiers [LET-7770] (#1258)
This commit is contained in:
@@ -1728,6 +1728,21 @@ export default function App({
|
||||
const [showExitStats, setShowExitStats] = useState(false);
|
||||
|
||||
const sharedReminderStateRef = useRef(createSharedReminderState());
|
||||
// Per-agent init progression — survives agent/conversation switches unlike SharedReminderState.
|
||||
const initProgressByAgentRef = useRef(
|
||||
new Map<string, { shallowCompleted: boolean; deepFired: boolean }>(),
|
||||
);
|
||||
const updateInitProgress = (
|
||||
forAgentId: string,
|
||||
update: Partial<{ shallowCompleted: boolean; deepFired: boolean }>,
|
||||
) => {
|
||||
const progress = initProgressByAgentRef.current.get(forAgentId) ?? {
|
||||
shallowCompleted: false,
|
||||
deepFired: false,
|
||||
};
|
||||
Object.assign(progress, update);
|
||||
initProgressByAgentRef.current.set(forAgentId, progress);
|
||||
};
|
||||
|
||||
// Track if we've set the conversation summary for this new conversation
|
||||
// Initialized to true for resumed conversations (they already have context)
|
||||
@@ -9198,9 +9213,6 @@ export default function App({
|
||||
|
||||
// Special handling for /init command
|
||||
if (trimmed === "/init") {
|
||||
// Manual /init supersedes pending auto-init for this agent
|
||||
autoInitPendingAgentIdsRef.current.delete(agentId);
|
||||
|
||||
const cmd = commandRunner.start(msg, "Gathering project context...");
|
||||
|
||||
// Check for pending approvals before either path
|
||||
@@ -9229,6 +9241,7 @@ export default function App({
|
||||
workingDirectory: process.cwd(),
|
||||
memoryDir: getMemoryFilesystemRoot(agentId),
|
||||
gitContext,
|
||||
depth: "deep",
|
||||
});
|
||||
|
||||
const { spawnBackgroundSubagentTask } = await import(
|
||||
@@ -9240,6 +9253,9 @@ export default function App({
|
||||
description: "Initializing memory",
|
||||
silentCompletion: true,
|
||||
onComplete: ({ success, error }) => {
|
||||
if (success) {
|
||||
updateInitProgress(agentId, { deepFired: true });
|
||||
}
|
||||
const msg = success
|
||||
? "Built a memory palace of you. Visit it with /palace."
|
||||
: `Memory initialization failed: ${error || "Unknown error"}`;
|
||||
@@ -9247,6 +9263,9 @@ export default function App({
|
||||
},
|
||||
});
|
||||
|
||||
// Clear pending auto-init only after spawn succeeded
|
||||
autoInitPendingAgentIdsRef.current.delete(agentId);
|
||||
|
||||
cmd.finish(
|
||||
"Learning about you and your codebase in the background. You'll be notified when ready.",
|
||||
true,
|
||||
@@ -9272,6 +9291,7 @@ export default function App({
|
||||
}
|
||||
} else {
|
||||
// Legacy path: primary agent processConversation
|
||||
autoInitPendingAgentIdsRef.current.delete(agentId);
|
||||
setCommandRunning(true);
|
||||
try {
|
||||
cmd.finish(
|
||||
@@ -9407,6 +9427,9 @@ export default function App({
|
||||
if (autoInitPendingAgentIdsRef.current.has(agentId) && !isSystemOnly) {
|
||||
try {
|
||||
const fired = await fireAutoInit(agentId, ({ success, error }) => {
|
||||
if (success) {
|
||||
updateInitProgress(agentId, { shallowCompleted: true });
|
||||
}
|
||||
const msg = success
|
||||
? "Built a memory palace of you. Visit it with /palace."
|
||||
: `Memory initialization failed: ${error || "Unknown error"}`;
|
||||
@@ -9542,10 +9565,59 @@ ${SYSTEM_REMINDER_CLOSE}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const maybeLaunchDeepInitSubagent = async () => {
|
||||
if (!memfsEnabledForAgent) return false;
|
||||
if (hasActiveInitSubagent()) return false;
|
||||
try {
|
||||
const gitContext = gatherGitContext();
|
||||
const initPrompt = buildMemoryInitRuntimePrompt({
|
||||
agentId,
|
||||
workingDirectory: process.cwd(),
|
||||
memoryDir: getMemoryFilesystemRoot(agentId),
|
||||
gitContext,
|
||||
depth: "deep",
|
||||
});
|
||||
const { spawnBackgroundSubagentTask } = await import(
|
||||
"../tools/impl/Task"
|
||||
);
|
||||
spawnBackgroundSubagentTask({
|
||||
subagentType: "init",
|
||||
prompt: initPrompt,
|
||||
description: "Deep memory initialization",
|
||||
silentCompletion: true,
|
||||
onComplete: ({ success, error }) => {
|
||||
if (success) {
|
||||
updateInitProgress(agentId, { deepFired: true });
|
||||
}
|
||||
const msg = success
|
||||
? "Built a memory palace of you. Visit it with /palace."
|
||||
: `Deep memory initialization failed: ${error || "Unknown error"}`;
|
||||
appendTaskNotificationEvents([msg]);
|
||||
},
|
||||
});
|
||||
debugLog("memory", "Auto-launched deep init subagent");
|
||||
return true;
|
||||
} catch (error) {
|
||||
debugWarn(
|
||||
"memory",
|
||||
`Failed to auto-launch deep init subagent: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
syncReminderStateFromContextTracker(
|
||||
sharedReminderStateRef.current,
|
||||
contextTrackerRef.current,
|
||||
);
|
||||
// Hydrate init progression from the per-agent map into the shared state
|
||||
// so the deep-init provider sees the correct flags for the current agent.
|
||||
const initProgress = initProgressByAgentRef.current.get(agentId);
|
||||
sharedReminderStateRef.current.shallowInitCompleted =
|
||||
initProgress?.shallowCompleted ?? false;
|
||||
sharedReminderStateRef.current.deepInitFired =
|
||||
initProgress?.deepFired ?? false;
|
||||
const { getSkillSources } = await import("../agent/context");
|
||||
const { parts: sharedReminderParts } = await buildSharedReminderParts({
|
||||
mode: "interactive",
|
||||
@@ -9561,11 +9633,11 @@ ${SYSTEM_REMINDER_CLOSE}
|
||||
skillSources: getSkillSources(),
|
||||
resolvePlanModeReminder: getPlanModeReminder,
|
||||
maybeLaunchReflectionSubagent,
|
||||
maybeLaunchDeepInitSubagent,
|
||||
});
|
||||
for (const part of sharedReminderParts) {
|
||||
reminderParts.push(part);
|
||||
}
|
||||
|
||||
// Build conversation switch alert if a switch is pending (behind feature flag)
|
||||
let conversationSwitchAlert = "";
|
||||
if (
|
||||
|
||||
@@ -76,6 +76,25 @@ ${recentCommits}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Depth instructions ────────────────────────────────────
|
||||
|
||||
const SHALLOW_INSTRUCTIONS = `
|
||||
Shallow init — fast project basics only (~5 tool calls max):
|
||||
- Only read: CLAUDE.md, AGENTS.md, package.json/pyproject.toml/Cargo.toml, README.md (first 100 lines), top-level directory listing
|
||||
- Detect user identity from the git context provided above (already in the prompt — no extra calls)
|
||||
- Run one git call: git log --format="%an <%ae>" | sort -u | head -5
|
||||
- Write exactly 4 files: project/overview.md, project/commands.md, project/conventions.md, human/identity.md
|
||||
- Skip: deep directory exploration, architecture mapping, config analysis, historical sessions, persona files, reflection/checkpoint phase
|
||||
`.trim();
|
||||
|
||||
const DEEP_INSTRUCTIONS = `
|
||||
Deep init — full exploration (follow the initializing-memory skill fully):
|
||||
- Read all existing memory files first — do NOT recreate what already exists
|
||||
- Then follow the full initializing-memory skill as your operating guide
|
||||
- Expand and deepen existing shallow files, add new ones to reach 15-25 target
|
||||
- If shallow init already ran, build on its output rather than starting over
|
||||
`.trim();
|
||||
|
||||
// ── Prompt builders ────────────────────────────────────────
|
||||
|
||||
/** Prompt for the background init subagent (MemFS path). */
|
||||
@@ -84,7 +103,9 @@ export function buildMemoryInitRuntimePrompt(args: {
|
||||
workingDirectory: string;
|
||||
memoryDir: string;
|
||||
gitContext: string;
|
||||
depth?: "shallow" | "deep";
|
||||
}): string {
|
||||
const depth = args.depth ?? "deep";
|
||||
return `
|
||||
The user ran /init for the current project.
|
||||
|
||||
@@ -92,6 +113,7 @@ Runtime context:
|
||||
- parent_agent_id: ${args.agentId}
|
||||
- working_directory: ${args.workingDirectory}
|
||||
- memory_dir: ${args.memoryDir}
|
||||
- research_depth: ${depth}
|
||||
|
||||
Git/project context:
|
||||
${args.gitContext}
|
||||
@@ -99,6 +121,8 @@ ${args.gitContext}
|
||||
Task:
|
||||
Initialize or reorganize the parent agent's filesystem-backed memory for this project.
|
||||
|
||||
${depth === "shallow" ? SHALLOW_INSTRUCTIONS : DEEP_INSTRUCTIONS}
|
||||
|
||||
Instructions:
|
||||
- Use the pre-loaded initializing-memory skill as your operating guide
|
||||
- Inspect existing memory before editing
|
||||
@@ -126,6 +150,7 @@ export async function fireAutoInit(
|
||||
workingDirectory: process.cwd(),
|
||||
memoryDir: getMemoryFilesystemRoot(agentId),
|
||||
gitContext,
|
||||
depth: "shallow",
|
||||
});
|
||||
|
||||
const { spawnBackgroundSubagentTask } = await import("../../tools/impl/Task");
|
||||
|
||||
@@ -12,6 +12,7 @@ export type SharedReminderId =
|
||||
| "plan-mode"
|
||||
| "reflection-step-count"
|
||||
| "reflection-compaction"
|
||||
| "deep-init"
|
||||
| "command-io"
|
||||
| "toolset-change"
|
||||
| "auto-init";
|
||||
@@ -65,6 +66,12 @@ export const SHARED_REMINDER_CATALOG: ReadonlyArray<SharedReminderDefinition> =
|
||||
"Compaction-triggered reflection reminder/auto-launch behavior",
|
||||
modes: ["interactive", "headless-one-shot", "headless-bidirectional"],
|
||||
},
|
||||
{
|
||||
id: "deep-init",
|
||||
description:
|
||||
"Auto-launch deep memory init after shallow init + turn gate",
|
||||
modes: ["interactive"],
|
||||
},
|
||||
{
|
||||
id: "command-io",
|
||||
description: "Recent slash command input/output context",
|
||||
|
||||
@@ -46,6 +46,7 @@ export interface SharedReminderContext {
|
||||
maybeLaunchReflectionSubagent?: (
|
||||
triggerSource: ReflectionTriggerSource,
|
||||
) => Promise<boolean>;
|
||||
maybeLaunchDeepInitSubagent?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
export type ReminderTextPart = { type: "text"; text: string };
|
||||
@@ -265,6 +266,29 @@ async function buildAutoInitReminder(
|
||||
return AUTO_INIT_REMINDER;
|
||||
}
|
||||
|
||||
// Disabled: deep init at turn 8 + reflection at turn 10 is too chaotic.
|
||||
// Re-enable once both subagent prompts are tuned to coexist.
|
||||
const DEEP_INIT_AUTO_LAUNCH_ENABLED = false;
|
||||
|
||||
async function maybeLaunchDeepInit(
|
||||
context: SharedReminderContext,
|
||||
): Promise<string | null> {
|
||||
if (!DEEP_INIT_AUTO_LAUNCH_ENABLED) return null;
|
||||
if (!context.state.shallowInitCompleted) return null;
|
||||
if (context.state.deepInitFired) return null;
|
||||
if (context.state.turnCount < 8) return null;
|
||||
|
||||
const memfsEnabled = settingsManager.isMemfsEnabled(context.agent.id);
|
||||
if (!memfsEnabled) return null;
|
||||
|
||||
if (context.maybeLaunchDeepInitSubagent) {
|
||||
// Don't latch deepInitFired here — it's set in the onComplete callback
|
||||
// only on success, so a failed deep init allows automatic retry.
|
||||
await context.maybeLaunchDeepInitSubagent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const MAX_COMMAND_REMINDERS_PER_TURN = 10;
|
||||
const MAX_TOOLSET_REMINDERS_PER_TURN = 5;
|
||||
const MAX_COMMAND_INPUT_CHARS = 2000;
|
||||
@@ -379,6 +403,7 @@ export const sharedReminderProviders: Record<
|
||||
"plan-mode": buildPlanModeReminder,
|
||||
"reflection-step-count": buildReflectionStepReminder,
|
||||
"reflection-compaction": buildReflectionCompactionReminder,
|
||||
"deep-init": maybeLaunchDeepInit,
|
||||
"command-io": buildCommandIoReminder,
|
||||
"toolset-change": buildToolsetChangeReminder,
|
||||
"auto-init": buildAutoInitReminder,
|
||||
|
||||
@@ -32,6 +32,8 @@ export interface SharedReminderState {
|
||||
pendingAutoInitReminder: boolean;
|
||||
pendingCommandIoReminders: CommandIoReminder[];
|
||||
pendingToolsetChangeReminders: ToolsetChangeReminder[];
|
||||
shallowInitCompleted: boolean;
|
||||
deepInitFired: boolean;
|
||||
}
|
||||
|
||||
export function createSharedReminderState(): SharedReminderState {
|
||||
@@ -48,6 +50,8 @@ export function createSharedReminderState(): SharedReminderState {
|
||||
pendingAutoInitReminder: false,
|
||||
pendingCommandIoReminders: [],
|
||||
pendingToolsetChangeReminders: [],
|
||||
shallowInitCompleted: false,
|
||||
deepInitFired: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user