Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/pi-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
],
"//": "The build intentionally bundles typebox (no --external typebox) and keeps it a regular dependency. OMP's extension loader rewrites every bare `typebox` import to its own omptype-based shim, whose schemas are callable objects rather than JSON Schema; they fail structuredClone during tool registration, so an external typebox import stops Magic Context loading on OMP. src/typebox-bundling.test.ts guards this.",
"scripts": {
"build": "bun ../../scripts/check-bun.mjs && tsc -p ../retina-local-fs/tsconfig.build.json && bun ../../scripts/clean-dist-chunks.mjs dist index.js subagent-entry.js historian-calibration-extension.js transformers-web.js transformers-node-wasm.js && bun build ../plugin/src/features/magic-context/memory/transformers-web-entry.ts --outfile dist/transformers-web.js --target browser --format esm --external onnxruntime-web && bun ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts ../plugin/src/features/magic-context/memory/embedding-worker.ts ../plugin/src/features/magic-context/migration-worker.ts ../plugin/src/hooks/magic-context/auto-search-worker.ts --outdir dist --entry-naming '[name].[ext]' --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external node:sqlite",
"build": "bun ../../scripts/check-bun.mjs && tsc -p ../retina-local-fs/tsconfig.build.json && bun ../../scripts/clean-dist-chunks.mjs dist index.js subagent-entry.js historian-calibration-extension.js transformers-web.js transformers-node-wasm.js && bun build ../plugin/src/features/magic-context/memory/transformers-web-entry.ts --outfile dist/transformers-web.js --target browser --format esm --external onnxruntime-web && bun ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts ../plugin/src/features/magic-context/memory/embedding-worker.ts ../plugin/src/features/magic-context/migration-worker.ts ../plugin/src/hooks/magic-context/auto-search-worker.ts --outdir dist --entry-naming '[name].[ext]' --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @oh-my-pi/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external node:sqlite",
"build:e2e-argv": "bun ../../scripts/clean-dist-chunks.mjs ../../tests/docker/.generated subagent-runner-e2e.mjs && bun build src/subagent-runner.ts --outdir ../../tests/docker/.generated --entry-naming 'subagent-runner-e2e.mjs' --splitting --target node --format esm --external node:sqlite && node --input-type=module -e 'const { __test } = await import(\"../../tests/docker/.generated/subagent-runner-e2e.mjs\"); if (typeof __test.buildArgs !== \"function\" || typeof __test.resolvePiInvocation !== \"function\") throw new Error(\"argv renderer exports missing\"); console.log(\"Node argv renderer: 2 exports loaded\")'",
"typecheck": "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit",
"test": "bun ../../scripts/check-bun.mjs && bun install --frozen-lockfile && BUN_JSC_useOMGJIT=0 bun test --parallel=4 --timeout 30000",
Expand Down
37 changes: 37 additions & 0 deletions packages/pi-plugin/src/dreamer/pi-session-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,43 @@ describe("loadDefaultPiSessionApi", () => {
expect(await api.listSessions()).toEqual(["omp-bare-import"]);
}, 30000);

it("loads the legacy-scope package through the first bare-import fallback", async () => {
// Same harness as the OMP loader above, for the other host scope: each
// bare loader must actually resolve a package through node_modules and
// hand back the real session API, not merely be present in the list.
// The unique marker is what proves WHICH copy was imported -- a loader
// that silently resolved the repo's own devDependency instead of the
// fixture would return "9.9.9" and fail here.
const dir = createTestTempDir("pi-bare-import-").dir;
writeFixturePackage(
join(dir, "node_modules", "@earendil-works", "pi-coding-agent"),
{
manifest: {
name: PI_SPEC,
version: "0.84.1",
exports: { ".": { import: "./index.js" } },
},
files: { "index.js": fixtureModule("pi-bare-import") },
},
);
const resolverCopy = join(dir, "src", "pi-session-api.ts");
mkdirSync(dirname(resolverCopy), { recursive: true });
copyFileSync(
fileURLToPath(new URL("./pi-session-api.ts", import.meta.url)),
resolverCopy,
);

const resolver = (await import(
pathToFileURL(resolverCopy).href
)) as typeof import("./pi-session-api");
const bareLoader = resolver.defaultLoaders.find(
(loader) => loader.name === "Bare import",
);
if (!bareLoader) throw new Error("bare-import loader missing");
const api = await resolver.loadDefaultPiSessionApi([bareLoader]);
expect(await api.listSessions()).toEqual(["pi-bare-import"]);
}, 30000);

it("resolves through a bin-shim symlink when argv[1] is the shim path", async () => {
const dir = createTestTempDir("pi-symlink-test-").dir;
const pkgRoot = join(
Expand Down
25 changes: 23 additions & 2 deletions packages/pi-plugin/src/dreamer/pi-session-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,33 @@ export const defaultLoaders: ModuleLoader[] = [
},
},
{
// A static import cannot work here: this package is a peer of whichever
// host loaded the extension, and it is absent in the others, so the three
// loaders must probe in order and only the winner is ever evaluated.
//
// The specifier MUST stay a string literal, not the PI_CODING_AGENT_MODULE
// constant. OMP resolves host packages for legacy extensions by rewriting
// the extension SOURCE TEXT, and it only records a reference when the
// specifier parses as a StringLiteral (@oh-my-pi/pi-coding-agent
// src/extensibility/plugins/legacy-pi-compat.ts
// `collectExtensionSpecifierReferences` -> `record("import", node.source)`).
// A variable specifier is invisible to that pass, and inside the shipped
// `omp` binary (a `bun build --compile` executable whose own resolver does
// not reach an on-disk node_modules) the import then fails outright with
// "Cannot find package '@earendil-works/pi-coding-agent' imported from
// ~/.omp/plugins/node_modules/@cortexkit/pi-magic-context/dist/index.js",
// which took down the [session-projects] backfill and the dreamer
// retrospective. Bun keeps both literals runtime-resolved: they are
// `--external` in packages/pi-plugin/package.json `build`.
name: "Bare import",
load: async () => await import(/* @vite-ignore */ PI_CODING_AGENT_MODULE),
load: async () =>
await import(/* @vite-ignore */ "@earendil-works/pi-coding-agent"),
},
{
// Same contract in OMP's canonical scope.
name: "Bare import (OMP)",
load: async () => await import(/* @vite-ignore */ OMP_CODING_AGENT_MODULE),
load: async () =>
await import(/* @vite-ignore */ "@oh-my-pi/pi-coding-agent"),
},
];

Expand Down
29 changes: 29 additions & 0 deletions packages/pi-plugin/src/omp-host-modules.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Ambient declaration for OMP's canonical coding-agent package.
*
* `dreamer/pi-session-api.ts` imports this specifier as a STRING LITERAL so the
* OMP legacy-extension loader can rewrite it (that loader only records
* references whose specifier parses as a StringLiteral), and the build keeps it
* `--external` so it is resolved at runtime rather than bundled. It is
* deliberately NOT a dependency: the shipped `omp` binary is a
* `bun build --compile` executable whose host modules live inside the
* executable, and adding a real devDependency here would pin a second,
* divergent copy of the host API -- the exact drift this resolver exists to
* avoid (see the header of dreamer/pi-session-api.ts). TypeScript can still
* only accept the literal import against a declaration, so it is declared here.
*
* The members are the ones the resolver probes, and the ones the running host
* actually exposes: loading the extension under `omp` and awaiting the bare
* import yields `SessionManager=function parseSessionEntries=function
* loadEntriesFromFile=function`. Same role as OMP's own
* `src/extensibility/plugins/legacy-pi-virtual-modules.d.ts`.
*/
declare module "@oh-my-pi/pi-coding-agent" {
export const SessionManager: {
listAll(sessionDir?: string): unknown[] | Promise<unknown[]>;
};
export function loadEntriesFromFile(
filePath: string,
): unknown[] | Promise<unknown[]>;
export function parseSessionEntries(content: string): unknown[];
}
Loading
Loading