Phase 7: add sendImmediateReinjectIdle — first skill triggers turn, rest as followUp.

Extracts buildReinjectBlocks for shared expand logic; immediate delivery when the agent is idle uses sendUserMessage without deliverAs for the first block only.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 11:58:15 +07:00
parent 2059f6033b
commit 446a186431
+31 -6
View File
@@ -1,5 +1,6 @@
import type { import type {
BeforeAgentStartEventResult, BeforeAgentStartEventResult,
ExtensionAPI,
ExtensionContext, ExtensionContext,
SessionCompactEvent, SessionCompactEvent,
Skill, Skill,
@@ -59,16 +60,16 @@ export function enqueueDeferredReinjectFromCompact(
); );
} }
/** Combined skill-block user text for pending names in queue order (SPEC §5.3). */ /** Expanded skill-block messages in queue order (SPEC §5.3). */
export function buildDeferredReinjectContent( export function buildReinjectBlocks(
pendingNames: readonly string[], skillNames: readonly string[],
state: ExtensionState, state: ExtensionState,
settings: SkillReinjectSettings, settings: SkillReinjectSettings,
registeredSkills: readonly Pick<Skill, "name" | "filePath" | "baseDir">[], registeredSkills: readonly Pick<Skill, "name" | "filePath" | "baseDir">[],
): string { ): string[] {
const registeredByName = new Map(registeredSkills.map((skill) => [skill.name, skill])); const registeredByName = new Map(registeredSkills.map((skill) => [skill.name, skill]));
const blocks: string[] = []; const blocks: string[] = [];
for (const name of pendingNames) { for (const name of skillNames) {
const tracked = state.skills.find((skill) => skill.name === name); const tracked = state.skills.find((skill) => skill.name === name);
const registered = registeredByName.get(name); const registered = registeredByName.get(name);
if (!tracked || !registered) { if (!tracked || !registered) {
@@ -85,7 +86,31 @@ export function buildDeferredReinjectContent(
), ),
); );
} }
return blocks.join("\n\n"); return blocks;
}
/** Combined skill-block user text for pending names in queue order (SPEC §5.3). */
export function buildDeferredReinjectContent(
pendingNames: readonly string[],
state: ExtensionState,
settings: SkillReinjectSettings,
registeredSkills: readonly Pick<Skill, "name" | "filePath" | "baseDir">[],
): string {
return buildReinjectBlocks(pendingNames, state, settings, registeredSkills).join("\n\n");
}
/** Immediate path when agent is idle: first block triggers turn, rest queue as followUp (SPEC §6.5.2). */
export function sendImmediateReinjectIdle(
pi: ExtensionAPI,
skillNames: readonly string[],
state: ExtensionState,
settings: SkillReinjectSettings,
registeredSkills: readonly Pick<Skill, "name" | "filePath" | "baseDir">[],
): void {
const blocks = buildReinjectBlocks(skillNames, state, settings, registeredSkills);
blocks.forEach((block, index) => {
pi.sendUserMessage(block, index === 0 ? undefined : { deliverAs: "followUp" });
});
} }
/** /**