feat(cross-cutting): added shell minimizer planning from AST command parsing

- Added AST-based minimizer planning with brush-parser to classify commands before minimization.
- Added original text capture and byte-metric `minimized` telemetry to shell execution results.
- Added minimizer configuration controls, including trust-gated loading via `settingsHash` and limits.
- Added `onMinimizedSave` callback wiring to persist minimized outputs and annotate Bash sinks with artifact IDs.
This commit is contained in:
can1357
2026-04-24 09:15:18 +02:00
parent 151934e9a1
commit 084ffd2058
11 changed files with 401 additions and 14 deletions
@@ -22,6 +22,17 @@ export interface BashExecutorOptions {
/** Artifact path/id for full output storage */
artifactPath?: string;
artifactId?: string;
/**
* Invoked when the native minimizer rewrote the command's output, giving
* the caller a chance to persist the lossless original capture (typically
* via the session's `ArtifactManager`). The returned id is spliced into
* the sink output as `artifact://<id>` so the agent can retrieve the raw
* bytes. Return `undefined` to skip the footer.
*/
onMinimizedSave?: (
originalText: string,
info: { filter: string; inputBytes: number; outputBytes: number },
) => Promise<string | undefined>;
}
export interface BashResult {
@@ -223,6 +234,21 @@ export async function executeBash(command: string, options?: BashExecutorOptions
};
}
// When the native minimizer rewrote the output, persist the original
// as a session artifact and splice an `artifact://<id>` footer into the
// visible text so the agent can retrieve the raw bytes losslessly.
const minimized = winner.result.minimized;
if (minimized && options?.onMinimizedSave) {
const artifactId = await options.onMinimizedSave(minimized.originalText, {
filter: minimized.filter,
inputBytes: minimized.inputBytes,
outputBytes: minimized.outputBytes,
});
if (artifactId) {
sink.push(`\n… full output: artifact://${artifactId} (${minimized.inputBytes} → ${minimized.outputBytes} bytes)\n`);
}
}
// Normal completion
return {
exitCode: winner.result.exitCode,