Skip to content

Commit ae943fa

Browse files
committed
feat(compaction-off): S5 commands + sidebar — honest surfaces (refusals, raw-usage label, archived-static compartments) (#266)
2 parents 52a0d99 + c8270cc commit ae943fa

15 files changed

Lines changed: 1391 additions & 747 deletions

File tree

‎packages/plugin/src/features/builtin-commands/commands.ts‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import type { BuiltinCommandConfig } from "./types";
22

3-
export function getMagicContextBuiltinCommands(): BuiltinCommandConfig {
3+
const COMPACTION_ENABLED_PATH = `compaction${".enabled"}`;
4+
5+
export function getMagicContextBuiltinCommands(compactionEnabled = true): BuiltinCommandConfig {
6+
const unavailableInCompactionOff = (command: string) =>
7+
`Unavailable when ${COMPACTION_ENABLED_PATH} is false: /${command} manages compacted history.`;
8+
49
return {
510
"ctx-status": {
611
template: "ctx-status",
712
description: "Show magic context status, pending queue, cache TTL, and debug info",
813
},
914
"ctx-recomp": {
1015
template: "ctx-recomp",
11-
description:
12-
"Rebuild compartments and facts from raw history (full or <start>-<end> range)",
16+
description: compactionEnabled
17+
? "Rebuild compartments and facts from raw history (full or <start>-<end> range)"
18+
: unavailableInCompactionOff("ctx-recomp"),
1319
},
1420
"ctx-wrapup": {
1521
template: "ctx-wrapup",
16-
description: "Compact older live history while keeping the newest messages raw",
22+
description: compactionEnabled
23+
? "Compact older live history while keeping the newest messages raw"
24+
: unavailableInCompactionOff("ctx-wrapup"),
1725
},
1826
"ctx-session-upgrade": {
1927
template: "ctx-session-upgrade",
@@ -22,7 +30,9 @@ export function getMagicContextBuiltinCommands(): BuiltinCommandConfig {
2230
},
2331
"ctx-flush": {
2432
template: "ctx-flush",
25-
description: "Force-process all pending magic context operations immediately",
33+
description: compactionEnabled
34+
? "Force-process all pending magic context operations immediately"
35+
: unavailableInCompactionOff("ctx-flush"),
2636
},
2737
"ctx-aug": {
2838
template: "ctx-aug",

‎packages/plugin/src/hooks/magic-context/command-handler.test.ts‎

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,200 @@ describe("createMagicContextCommandHandler", () => {
306306
);
307307
});
308308

309+
for (const command of ["ctx-wrapup", "ctx-recomp", "ctx-flush"] as const) {
310+
it(`refuses /${command} without side effects when compaction is off`, async () => {
311+
insertTag(db, "ses-compaction-off", 1, 500);
312+
insertPendingOp(db, "ses-compaction-off", 1);
313+
const sendNotification = mock(async () => {});
314+
const executeWrapup = mock(async () => "should not run");
315+
const executeRecomp = mock(async () => "should not run");
316+
const onFlush = mock(() => {});
317+
const handler = createMagicContextCommandHandler({
318+
db,
319+
protectedTags: 3,
320+
compactionOff: true,
321+
executeWrapup,
322+
executeRecomp,
323+
onFlush,
324+
sendNotification,
325+
});
326+
327+
await expectSentinel(
328+
handler["command.execute.before"](
329+
{ command, sessionID: "ses-compaction-off", arguments: "" },
330+
makeOutput(""),
331+
{},
332+
),
333+
`__CONTEXT_MANAGEMENT_${command.toUpperCase()}_HANDLED__`,
334+
);
335+
336+
expect(sendNotification).toHaveBeenCalledWith(
337+
"ses-compaction-off",
338+
`Magic Context compaction is disabled (compaction.enabled: false) — /${command} manages compacted history and has no effect in this mode.`,
339+
{},
340+
);
341+
expect(executeWrapup).not.toHaveBeenCalled();
342+
expect(executeRecomp).not.toHaveBeenCalled();
343+
expect(onFlush).not.toHaveBeenCalled();
344+
expect(getPendingOpsCount(db, "ses-compaction-off")).toBe(1);
345+
expect(getTagStatus(db, "ses-compaction-off", 1)).toBe("active");
346+
});
347+
}
348+
349+
it("keeps compaction commands functional when compaction is on", async () => {
350+
for (const command of ["ctx-wrapup", "ctx-recomp", "ctx-flush"] as const) {
351+
const sendNotification = mock(async () => {});
352+
const handler = createMagicContextCommandHandler({
353+
db,
354+
protectedTags: 3,
355+
compactionOff: false,
356+
executeWrapup: async () => "wrapup ran",
357+
executeRecomp: async () => "recomp ran",
358+
sendNotification,
359+
});
360+
361+
await expectSentinel(
362+
handler["command.execute.before"](
363+
{ command, sessionID: `ses-compaction-on-${command}`, arguments: "" },
364+
makeOutput(""),
365+
{},
366+
),
367+
`__CONTEXT_MANAGEMENT_${command.toUpperCase()}_HANDLED__`,
368+
);
369+
370+
const notifications = (sendNotification.mock.calls as Array<[string, string]>).map(
371+
([, text]) => text,
372+
);
373+
expect(notifications.join("\n")).not.toContain("Magic Context compaction is disabled");
374+
}
375+
});
376+
377+
describe("knowledge-layer commands in compaction-off mode", () => {
378+
it("keeps /ctx-status functional and labels the mode", async () => {
379+
const sendNotification = mock(async () => {});
380+
const handler = createMagicContextCommandHandler({
381+
db,
382+
protectedTags: 3,
383+
compactionOff: true,
384+
sendNotification,
385+
});
386+
387+
await expectSentinel(
388+
handler["command.execute.before"](
389+
{ command: "ctx-status", sessionID: "ses-status-off", arguments: "" },
390+
makeOutput(""),
391+
{},
392+
),
393+
"__CONTEXT_MANAGEMENT_CTX-STATUS_HANDLED__",
394+
);
395+
396+
expect(sendNotification).toHaveBeenCalledWith(
397+
"ses-status-off",
398+
expect.stringContaining(
399+
"**Compaction:** disabled (compaction.enabled: false) — native compaction owns the context window.",
400+
),
401+
{},
402+
);
403+
});
404+
405+
it("keeps /ctx-embed functional", async () => {
406+
const sendNotification = mock(async () => {});
407+
const handler = createMagicContextCommandHandler({
408+
db,
409+
protectedTags: 3,
410+
compactionOff: true,
411+
getEmbedStatusText: () => "embedding is ready",
412+
sendNotification,
413+
});
414+
415+
await expectSentinel(
416+
handler["command.execute.before"](
417+
{ command: "ctx-embed", sessionID: "ses-embed-off", arguments: "" },
418+
makeOutput(""),
419+
{},
420+
),
421+
"__CONTEXT_MANAGEMENT_CTX-EMBED_HANDLED__",
422+
);
423+
424+
expect(sendNotification).toHaveBeenCalledWith(
425+
"ses-embed-off",
426+
"## Embedding Status\n\nembedding is ready",
427+
{},
428+
);
429+
});
430+
431+
it("keeps /ctx-dream functional", async () => {
432+
const sendNotification = mock(async () => {});
433+
const runManual = mock(async () => ({
434+
ran: ["verify"],
435+
skippedNoWork: [],
436+
deferredBusy: [],
437+
failed: [],
438+
}));
439+
const handler = createMagicContextCommandHandler({
440+
db,
441+
protectedTags: 3,
442+
compactionOff: true,
443+
sendNotification,
444+
dreamer: { config: {} as never, projectPath: "/repo", runManual },
445+
});
446+
447+
await expectSentinel(
448+
handler["command.execute.before"](
449+
{ command: "ctx-dream", sessionID: "ses-dream-off", arguments: "" },
450+
makeOutput(""),
451+
{},
452+
),
453+
"__CONTEXT_MANAGEMENT_CTX-DREAM_HANDLED__",
454+
);
455+
456+
expect(runManual).toHaveBeenCalledWith(undefined);
457+
});
458+
459+
it("keeps /ctx-aug functional", async () => {
460+
const sendNotification = mock(async () => {});
461+
const client = {
462+
session: {
463+
create: mock(async () => ({ data: { id: "sidekick-child" } })),
464+
promptAsync: mock(async () => undefined),
465+
messages: mock(async () => ({
466+
data: [
467+
{
468+
info: { role: "assistant", time: { created: Date.now() } },
469+
parts: [{ type: "text", text: "Use Bun" }],
470+
},
471+
],
472+
})),
473+
delete: mock(async () => ({ data: undefined })),
474+
},
475+
};
476+
const handler = createMagicContextCommandHandler({
477+
db,
478+
protectedTags: 3,
479+
compactionOff: true,
480+
sendNotification,
481+
sidekick: {
482+
config: { timeout_ms: 5_000 },
483+
projectPath: "/repo",
484+
sessionDirectory: "/repo",
485+
client: client as never,
486+
},
487+
});
488+
489+
await expectSentinel(
490+
handler["command.execute.before"](
491+
{ command: "ctx-aug", sessionID: "ses-aug-off", arguments: "Check this" },
492+
makeOutput(""),
493+
{},
494+
),
495+
"__CONTEXT_MANAGEMENT_CTX-AUG_HANDLED__",
496+
);
497+
498+
expect(client.session.create).toHaveBeenCalledTimes(1);
499+
expect(client.session.promptAsync).toHaveBeenCalledTimes(1);
500+
});
501+
});
502+
309503
describe("ctx-flush", () => {
310504
it("reports an empty queue", async () => {
311505
const sendNotification = mock(async () => {});

‎packages/plugin/src/hooks/magic-context/command-handler.ts‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export interface CommandExecuteOutput {
132132
}
133133

134134
const SENTINEL_PREFIX = "__CONTEXT_MANAGEMENT_";
135+
const COMPACTION_ENABLED_PATH = `compaction${".enabled"}`;
135136

136137
// Effect HTTP plain-string TypeIds (NOT Symbols), verified against effect 4.x
137138
// source. Because the guards are `key in obj` checks on string keys, a hand-built
@@ -467,6 +468,8 @@ async function executeDreaming(
467468
export function createMagicContextCommandHandler(deps: {
468469
db: Database;
469470
protectedTags: number;
471+
/** Boot-resolved mode; command paths must not re-read configuration. */
472+
compactionOff?: boolean;
470473
executeThresholdPercentage?: number | { default: number; [modelKey: string]: number };
471474
executeThresholdTokens?: { default?: number; [modelKey: string]: number | undefined };
472475
historyBudgetPercentage?: number;
@@ -596,6 +599,16 @@ export function createMagicContextCommandHandler(deps: {
596599
const sessionId = input.sessionID;
597600
let result = "";
598601

602+
if (deps.compactionOff && (isFlush || isRecomp || isWrapup)) {
603+
const command = `/${input.command}`;
604+
await deps.sendNotification(
605+
sessionId,
606+
`Magic Context compaction is disabled (${COMPACTION_ENABLED_PATH}: false) — ${command} manages compacted history and has no effect in this mode.`,
607+
{},
608+
);
609+
throwSentinel(input.command);
610+
}
611+
599612
if (isAug) {
600613
await executeAugmentation(deps, sessionId, input.arguments);
601614
return; // executeAugmentation throws sentinel internally
@@ -721,7 +734,10 @@ export function createMagicContextCommandHandler(deps: {
721734
liveContextLimit,
722735
);
723736
const moduleStatus = rustStatus ? `\n\n${formatRustStatusText(rustStatus)}` : "";
724-
const combinedStatus = `${statusOutput}${moduleStatus}`;
737+
const modeStatus = deps.compactionOff
738+
? `**Compaction:** disabled (${COMPACTION_ENABLED_PATH}: false) — native compaction owns the context window.\n\n`
739+
: "";
740+
const combinedStatus = `${modeStatus}${statusOutput}${moduleStatus}`;
725741
result += result ? `\n\n${combinedStatus}` : combinedStatus;
726742
}
727743

‎packages/plugin/src/hooks/magic-context/hook.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ export function createMagicContextHook(deps: MagicContextDeps) {
10901090
const commandHandler = createMagicContextCommandHandler({
10911091
db,
10921092
protectedTags: deps.config.protected_tags,
1093+
compactionOff,
10931094
toastDurationMs: deps.config.toast_duration_ms,
10941095
executeThresholdPercentage: deps.config.execute_threshold_percentage ?? 65,
10951096
executeThresholdTokens: deps.config.execute_threshold_tokens,

‎packages/plugin/src/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ const server: Plugin = async (ctx) => {
618618
// for permission precedence and hard `steps`/`maxSteps` cap semantics.
619619
const commandConfig = {
620620
...(config.command ?? {}),
621-
...getMagicContextBuiltinCommands(),
621+
...getMagicContextBuiltinCommands(isCompactionEnabled(pluginConfig)),
622622
...(pluginConfig.command ?? {}),
623623
};
624624

‎packages/plugin/src/plugin/rpc-handlers.test.ts‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,84 @@ describe("buildSidebarSnapshot — Rust module status merge", () => {
270270
});
271271
});
272272

273+
describe("compaction-off sidebar RPC data", () => {
274+
test("reports the resolved mode and raw native usage independently of threshold fill", () => {
275+
const db = createTestDb();
276+
try {
277+
const sessionId = "ses-native-sidebar";
278+
db.prepare(
279+
`INSERT INTO session_meta (
280+
session_id, last_input_tokens, last_context_percentage,
281+
system_prompt_tokens, memory_block_count
282+
) VALUES (?, 63077, 97, 0, 0)`,
283+
).run(sessionId);
284+
replaceAllCompartmentState(
285+
db,
286+
sessionId,
287+
[
288+
{
289+
sequence: 0,
290+
startMessage: 1,
291+
endMessage: 4,
292+
startMessageId: "msg-1",
293+
endMessageId: "msg-4",
294+
title: "Archived",
295+
content: "Historical context retained for later expansion.",
296+
},
297+
],
298+
[],
299+
);
300+
301+
const snapshot = buildSidebarSnapshot(
302+
db,
303+
sessionId,
304+
process.cwd(),
305+
undefined,
306+
4000,
307+
{ execute_threshold_percentage: 65 },
308+
{
309+
usage: {
310+
current_total_input_tokens: 41_000,
311+
context_limit_tokens: 100_000,
312+
},
313+
},
314+
false,
315+
);
316+
const detail = buildStatusDetail(
317+
db,
318+
sessionId,
319+
process.cwd(),
320+
undefined,
321+
{ execute_threshold_percentage: 65 },
322+
undefined,
323+
4000,
324+
{
325+
usage: {
326+
current_total_input_tokens: 41_000,
327+
context_limit_tokens: 100_000,
328+
},
329+
},
330+
false,
331+
);
332+
const thresholdFillPercentage = (41_000 / (100_000 * 0.65)) * 100;
333+
334+
expect(snapshot.compaction_enabled).toBe(false);
335+
expect(detail.compaction_enabled).toBe(false);
336+
expect(snapshot.native_context_usage_percentage).toBe(41);
337+
expect(detail.native_context_usage_percentage).toBe(41);
338+
expect(snapshot.native_context_usage_percentage).not.toBeCloseTo(
339+
thresholdFillPercentage,
340+
);
341+
expect(snapshot.archivedCompartmentCount).toBe(1);
342+
343+
const enabledDetail = buildStatusDetail(db, "ses-native-sidebar-on", process.cwd());
344+
expect(enabledDetail.compaction_enabled).toBe(true);
345+
} finally {
346+
closeQuietly(db);
347+
}
348+
});
349+
});
350+
273351
describe("buildStatusDetail — history token reuse (council audit bg_51106601 #1)", () => {
274352
test("sets historyBlockTokens from compartmentTokens only (facts retired in v2)", () => {
275353
const db = createTestDb();

0 commit comments

Comments
 (0)