| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- /**
- * Human-facing `/compact` command over the backend-independent compaction seam.
- * @module @deepseek-ai/dsh-command-compact
- */
- import type { Context } from '@deepseek-ai/cordis'
- import { ManualCompactionError } from '@deepseek-ai/dsh-compaction'
- import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands'
- export const name = 'command-compact'
- export const inject = ['commands', 'compaction']
- const USAGE = 'Usage: /compact (no arguments)'
- /** Fail loudly if a locally closed union gains an unhandled member. */
- /* v8 ignore start -- closed-union backstop is unreachable without violating the TypeScript contract */
- function assertNever(value: never): never {
- throw new TypeError(`unknown manual compaction error code: ${String(value)}`)
- }
- /* v8 ignore stop */
- /** Convert expected capability failures into concise human-only outcomes. */
- function expectedFailure(error: ManualCompactionError): CommandResult {
- switch (error.code) {
- case 'busy':
- return {
- kind: 'error',
- text: 'Compaction is unavailable because this process has an active compaction, or the agent is not idle.',
- }
- case 'cancelled':
- return { kind: 'error', text: 'Compaction cancelled.' }
- case 'changed':
- return {
- kind: 'error',
- text: 'The history selected for compaction changed before it could be replaced. The conversation is unchanged; the attempt is recorded in the session log.',
- }
- case 'summary':
- return {
- kind: 'error',
- text: 'Compaction could not produce a useful summary. The conversation is unchanged; the attempt is recorded in the session log.',
- }
- case 'commit':
- return {
- kind: 'error',
- text: 'Compaction did not finish cleanly; some session history may have changed. Inspect the current session state before retrying.',
- }
- case 'persistence':
- return {
- kind: 'error',
- text: 'Compaction finished, but the session could not be saved.',
- }
- /* v8 ignore next 2 -- ManualCompactionErrorCode is closed and every member is handled above */
- default: return assertNever(error.code)
- }
- }
- /** Execute one argument-free manual compaction request. */
- async function executeCompact(
- ctx: Context,
- invocation: CommandInvocation,
- ): Promise<CommandResult> {
- if (invocation.rawInput.trim().length > 0) {
- return { kind: 'error', text: USAGE }
- }
- try {
- const result = await ctx.compaction.compactNow(invocation.agent, invocation.signal, invocation.commandId)
- if (result === null) return { kind: 'success', text: 'No compactable history yet.' }
- return {
- kind: 'success',
- text: `Compacted ${result.shadowedSeqs.length} history items (~${result.shadowedTokenCount} tokens).`,
- sourceEventSeq: result.summarySeq,
- }
- } catch (error: unknown) {
- if (invocation.signal.aborted) return { kind: 'error', text: 'Compaction cancelled.' }
- if (error instanceof ManualCompactionError) return expectedFailure(error)
- throw error
- }
- }
- /**
- * Register `/compact` for every composed human-command adapter.
- * @param ctx - context carrying the command registry and the compaction seam.
- */
- export function apply(ctx: Context): void {
- const active = new Set<Promise<CommandResult>>()
- const handler = (invocation: CommandInvocation): Promise<CommandResult> => {
- const operation = executeCompact(ctx, invocation)
- active.add(operation)
- const retire = (): void => { active.delete(operation) }
- // Both branches retire without rethrowing, so the derived observer promise
- // cannot become an unhandled mirror of an expected handler rejection.
- void operation.then(retire, retire)
- return operation
- }
- ctx.effect(function* () {
- // Yield drain before registration: composite teardown is LIFO, so no new
- // invocation can enter while already-started handler promises quiesce.
- yield async () => { await Promise.allSettled(active) }
- yield ctx.commands.register({
- name: 'compact',
- description: 'Compact older conversation history',
- handler,
- })
- }, 'command-compact lifecycle')
- }
|