Merge PR #6507: fix(plugins): restore pi-sprite legacy compatibility (@roboomp)

This commit is contained in:
can1357
2026-07-26 15:41:31 +02:00
5 changed files with 98 additions and 3 deletions
+3
View File
@@ -90,6 +90,9 @@
### Fixed
- Fixed `/context` counting hidden, explicit-only skills (`hide: true` / `disable-model-invocation`) in the Skills category and subtracting that inflated estimate from the first system-prompt block, which reported `System prompt: 0 tokens` and inflated Skills usage. Accounting now counts only the skills actually rendered into the system prompt — mirroring `buildSystemPrompt`'s filter, so hidden skills and all skills when the `read` tool is unavailable contribute zero ([#6498](https://github.com/can1357/oh-my-pi/issues/6498)).
### Fixed
- Fixed `pi-sprite` failing plugin validation because the legacy Pi compatibility shims omitted `createExtensionRuntime` and terminal capability/image-deletion helpers used by the extension ([#6506](https://github.com/can1357/oh-my-pi/issues/6506)).
## [17.1.1] - 2026-07-24
@@ -861,6 +861,11 @@ export interface ResourceLoader {
readonly __ompLegacyPiLoader?: true;
}
/** Create a pre-initialization runtime for legacy extension resource loaders. */
export function createExtensionRuntime(): ExtensionRuntime {
return new ExtensionRuntime();
}
/**
* Loader-owned inputs that {@link createAgentSession} needs regardless of
* whether the caller provided extra options. `cwd`/`agentDir` fall back to
@@ -894,7 +899,7 @@ export class DefaultResourceLoader implements ResourceLoader {
readonly __ompLegacyPiLoader = true as const;
#state: ResolvedLoaderState;
#options: DefaultResourceLoaderOptions;
#extensionsResult: LoadExtensionsResult = { extensions: [], errors: [], runtime: new ExtensionRuntime() };
#extensionsResult: LoadExtensionsResult = { extensions: [], errors: [], runtime: createExtensionRuntime() };
#skills: Skill[] = [];
#skillDiagnostics: ResourceDiagnostic[] = [];
#prompts: PromptTemplate[] = [];
@@ -1043,7 +1048,7 @@ export class DefaultResourceLoader implements ResourceLoader {
const { cwd, noExtensions, additionalExtensionPaths, extensionFactories, eventBus } = this.#state;
if (noExtensions && additionalExtensionPaths.length === 0 && extensionFactories.length === 0) {
return { extensions: [], errors: [], runtime: new ExtensionRuntime() };
return { extensions: [], errors: [], runtime: createExtensionRuntime() };
}
const paths = await discoverSessionExtensionPaths(
@@ -6,5 +6,31 @@
* exposes the equivalent, broader `decodePrintableKey` helper. Keep the legacy
* name available without reintroducing it into the canonical package surface.
*/
import { ImageProtocol, isInsideTmux, TERMINAL, wrapTmuxPassthrough } from "@oh-my-pi/pi-tui";
export * from "@oh-my-pi/pi-tui";
export { decodePrintableKey as decodeKittyPrintable } from "@oh-my-pi/pi-tui";
export {
decodePrintableKey as decodeKittyPrintable,
encodeKittyDeleteImage as deleteKittyImage,
} from "@oh-my-pi/pi-tui";
/** Report canonical terminal capabilities through the legacy Pi TUI shape. */
export function getCapabilities(): {
images: "kitty" | "iterm2" | null;
trueColor: boolean;
hyperlinks: boolean;
} {
const images =
TERMINAL.imageProtocol === ImageProtocol.Kitty
? "kitty"
: TERMINAL.imageProtocol === ImageProtocol.Iterm2
? "iterm2"
: null;
return { images, trueColor: TERMINAL.trueColor, hyperlinks: TERMINAL.hyperlinks };
}
/** Delete every Kitty graphics image using the legacy Pi TUI control sequence. */
export function deleteAllKittyImages(): string {
const sequence = "\x1b_Ga=d,d=A,q=2\x1b\\";
return isInsideTmux() ? wrapTmuxPassthrough(sequence) : sequence;
}
@@ -265,6 +265,28 @@ describe("legacy pi package root remaps (issue #1474)", () => {
expect(loaded.printable).toBe("a");
});
it("loads pi-sprite's legacy terminal helpers", async () => {
const entry = await writeFixtureExtension(
[
'import { deleteAllKittyImages, deleteKittyImage, getCapabilities } from "@earendil-works/pi-tui";',
"export const deleteOne = deleteKittyImage(42);",
"export const deleteAll = deleteAllKittyImages();",
"export const capabilities = getCapabilities();",
].join("\n"),
);
const loaded = (await loadLegacyPiModule(entry)) as {
deleteOne: string;
deleteAll: string;
capabilities: { images: "kitty" | "iterm2" | null; trueColor: boolean; hyperlinks: boolean };
};
expect(loaded.deleteOne).toContain("a=d,d=I,i=42,q=2");
expect(loaded.deleteAll).toContain("a=d,d=A,q=2");
expect(["kitty", "iterm2", null]).toContain(loaded.capabilities.images);
expect(typeof loaded.capabilities.trueColor).toBe("boolean");
expect(typeof loaded.capabilities.hyperlinks).toBe("boolean");
});
it("preserves legacy defineTool root imports and usable coding tools", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-legacy-coding-tools-"));
tempRoots.push(dir);
@@ -850,6 +850,45 @@ describe("legacy-pi in-place module loading (issue #1674)", () => {
});
});
it("exposes a fresh legacy extension runtime through the package root", async () => {
const dir = await writePackage({
"package.json": JSON.stringify({ name: "legacy-extension-runtime-ext", version: "1.0.0" }),
"index.ts": [
'import { createExtensionRuntime } from "@earendil-works/pi-coding-agent";',
"const first = createExtensionRuntime();",
"const second = createExtensionRuntime();",
"first.flagValues.set('sprite', true);",
"let initializationError;",
"try {",
" first.getActiveTools();",
"} catch (error) {",
" initializationError = error instanceof Error ? error.message : String(error);",
"}",
"export const runtimeContract = {",
" firstFlag: first.flagValues.get('sprite'),",
" secondHasFlag: second.flagValues.has('sprite'),",
" initializationError,",
"};",
"export default function (pi) { void pi; }",
].join("\n"),
});
const mod = (await loadLegacyPiModule(path.join(dir, "index.ts"))) as {
runtimeContract: {
firstFlag: boolean;
secondHasFlag: boolean;
initializationError: string;
};
};
expect(mod.runtimeContract).toEqual({
firstFlag: true,
secondHasFlag: false,
initializationError:
"Extension runtime not initialized. Action methods cannot be called during extension loading.",
});
});
it("honors legacy bash operations overrides", async () => {
const dir = await writePackage({
"package.json": JSON.stringify({ name: "legacy-bash-ops-ext", version: "1.0.0" }),