mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`, `verify-translation-pairing --write` for the touched bilingual pairs, `gen-doc-graphs`, and one typert snapshot whose ids embed character offsets. `pnpm run rescope-vendor --check` verifies the result. Renames nine vendored packages (cordis, cosmokit, schemastery and the six @cordisjs plugins) and every reference that resolves them: manifest names and dependency keys, module specifiers including declare-module merges, cordis.yml plugin names, tsconfig paths, every Markdown fence, and `docs/` prose. Directory names, upstream versions, and dependency ranges are unchanged, so vendor/README.md still reads as an upstream snapshot; its manifest table gains an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed at each fork's origin. The tutorial tier follows the rename end to end: its yaml fences named plugins the Loader can no longer resolve, its `ts ignore-check` fences disagreed with the compiled fences beside them, and its prose quoted both. The contracts that told readers to keep upstream names — the root convention and the vendoring cookbook's tree comment and manifest invariant — now say to rescope instead. Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle purity gate now names the vendored libraries a browser bundle inlines, and the files where a bare `cordis` is an agent-preset id keep that product data.
107 lines
4.0 KiB
TypeScript
107 lines
4.0 KiB
TypeScript
/**
|
|
* 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-compact'
|
|
import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands'
|
|
|
|
export const name = 'command-compact'
|
|
export const inject = ['commands', 'compact']
|
|
|
|
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.compact.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')
|
|
}
|