Phase 11: integration session override — SPEC §7.1, §16.4.

Persist autoCompactIntegration override in state and wire resolveDeliveryMode plus status delivery line.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 12:56:19 +07:00
parent 435c5b3289
commit 03dcdb22de
3 changed files with 46 additions and 2 deletions
+35 -1
View File
@@ -85,7 +85,7 @@ function showSkillReinjectStatus(ctx: ExtensionCommandContext, deps: SkillReinje
return; return;
} }
const settings = readSettings(ctx); const settings = readSettings(ctx);
ctx.ui.notify(formatSkillReinjectStatus(deps.state, settings, deps.runtime), "info"); ctx.ui.notify(formatSkillReinjectStatus(deps.state, settings, deps.runtime, deps.state.sessionIntegrationOverride), "info");
} }
async function handleSkillReinjectCommand( async function handleSkillReinjectCommand(
@@ -116,6 +116,10 @@ async function handleSkillReinjectCommand(
handleClearTrackedSkills(ctx, deps); handleClearTrackedSkills(ctx, deps);
return; return;
} }
if (subcommand === "integration") {
handleIntegrationOverride(trimmed, ctx, deps);
return;
}
if (ctx.hasUI) { if (ctx.hasUI) {
ctx.ui.notify(`skill-reinject: unknown subcommand "${subcommand}"`, "warning"); ctx.ui.notify(`skill-reinject: unknown subcommand "${subcommand}"`, "warning");
@@ -185,3 +189,33 @@ function handleClearTrackedSkills(ctx: ExtensionCommandContext, deps: SkillReinj
} }
ctx.ui.notify("skill-reinject: cleared tracked skills", "info"); ctx.ui.notify("skill-reinject: cleared tracked skills", "info");
} }
const INTEGRATION_VALUES: readonly AutoCompactIntegration[] = ["auto", "defer", "immediate", "off"];
function isAutoCompactIntegration(value: string): value is AutoCompactIntegration {
return (INTEGRATION_VALUES as readonly string[]).includes(value);
}
function handleIntegrationOverride(
args: string,
ctx: ExtensionCommandContext,
deps: SkillReinjectCommandDeps,
): void {
const mode = args.trim().split(/\s+/)[1];
if (!mode || !isAutoCompactIntegration(mode)) {
if (ctx.hasUI) {
ctx.ui.notify(
"skill-reinject: usage: /skill-reinject integration auto|defer|immediate|off",
"warning",
);
}
return;
}
deps.state.sessionIntegrationOverride = mode;
deps.persistState();
if (!ctx.hasUI) {
return;
}
const settings = readSettings(ctx);
ctx.ui.notify(formatDeliveryLine(settings, deps.runtime, mode), "info");
}
+1 -1
View File
@@ -92,7 +92,7 @@ export default function skillReinject(pi: ExtensionAPI): void {
} }
const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills); const skills = resolveRegisteredSkills(ctx.cwd, registeredSkills);
const deliveryMode = resolveDeliveryMode(settings, runtime); const deliveryMode = resolveDeliveryMode(settings, runtime, state.sessionIntegrationOverride);
if (deliveryMode === "defer") { if (deliveryMode === "defer") {
enqueueDeferredReinjectFromCompact(state, settings, ctx, event, skills); enqueueDeferredReinjectFromCompact(state, settings, ctx, event, skills);
persistState(); persistState();
+10
View File
@@ -21,6 +21,8 @@ export interface TrackedSkill {
export interface ExtensionState { export interface ExtensionState {
version: 1; version: 1;
sessionOverride: boolean | null; sessionOverride: boolean | null;
/** Session override for autoCompactIntegration (SPEC §7.1, §16.4). */
sessionIntegrationOverride: AutoCompactIntegration | null;
skills: TrackedSkill[]; skills: TrackedSkill[];
lastCompactionSource: CompactionSource | null; lastCompactionSource: CompactionSource | null;
/** Skill names awaiting re-inject on the next before_agent_start (SPEC §6.5). */ /** Skill names awaiting re-inject on the next before_agent_start (SPEC §6.5). */
@@ -49,6 +51,12 @@ function isExtensionState(data: unknown): data is ExtensionState {
return ( return (
candidate.version === 1 && candidate.version === 1 &&
(candidate.sessionOverride === null || typeof candidate.sessionOverride === "boolean") && (candidate.sessionOverride === null || typeof candidate.sessionOverride === "boolean") &&
(candidate.sessionIntegrationOverride === null ||
candidate.sessionIntegrationOverride === "auto" ||
candidate.sessionIntegrationOverride === "defer" ||
candidate.sessionIntegrationOverride === "immediate" ||
candidate.sessionIntegrationOverride === "off" ||
candidate.sessionIntegrationOverride === undefined) &&
Array.isArray(candidate.skills) && Array.isArray(candidate.skills) &&
(candidate.lastCompactionSource === null || (candidate.lastCompactionSource === null ||
candidate.lastCompactionSource === "auto" || candidate.lastCompactionSource === "auto" ||
@@ -76,6 +84,7 @@ export function createInitialState(): ExtensionState {
return { return {
version: 1, version: 1,
sessionOverride: null, sessionOverride: null,
sessionIntegrationOverride: null,
skills: [], skills: [],
lastCompactionSource: null, lastCompactionSource: null,
pendingReinject: [], pendingReinject: [],
@@ -85,6 +94,7 @@ export function createInitialState(): ExtensionState {
/** Copy persisted fields into live session state (SPEC §6.3). */ /** Copy persisted fields into live session state (SPEC §6.3). */
export function applyExtensionState(target: ExtensionState, loaded: ExtensionState): void { export function applyExtensionState(target: ExtensionState, loaded: ExtensionState): void {
target.sessionOverride = loaded.sessionOverride; target.sessionOverride = loaded.sessionOverride;
target.sessionIntegrationOverride = loaded.sessionIntegrationOverride ?? null;
target.skills = loaded.skills; target.skills = loaded.skills;
target.lastCompactionSource = loaded.lastCompactionSource; target.lastCompactionSource = loaded.lastCompactionSource;
target.pendingReinject = loaded.pendingReinject; target.pendingReinject = loaded.pendingReinject;