@oh-my-pi/pi-coding-agent 17.0.1 → 17.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +162 -40
- package/dist/cli.js +4559 -4543
- package/dist/types/advisor/config.d.ts +14 -6
- package/dist/types/advisor/runtime.d.ts +20 -11
- package/dist/types/autolearn/controller.d.ts +1 -0
- package/dist/types/config/model-discovery.d.ts +17 -0
- package/dist/types/config/model-resolver.d.ts +6 -2
- package/dist/types/config/settings-schema.d.ts +39 -10
- package/dist/types/config/settings.d.ts +50 -0
- package/dist/types/cursor.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +5 -2
- package/dist/types/eval/agent-bridge.d.ts +7 -19
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +18 -0
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +55 -1
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/extensibility/utils.d.ts +2 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/types.d.ts +3 -0
- package/dist/types/mcp/transports/stdio.d.ts +42 -1
- package/dist/types/modes/components/advisor-config.d.ts +8 -1
- package/dist/types/modes/components/agent-hub.d.ts +15 -0
- package/dist/types/modes/components/hook-editor.d.ts +7 -0
- package/dist/types/modes/components/model-hub.d.ts +5 -2
- package/dist/types/modes/components/session-selector.d.ts +2 -0
- package/dist/types/modes/controllers/command-controller.d.ts +8 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/print-mode.d.ts +1 -1
- package/dist/types/modes/warp-events.d.ts +24 -0
- package/dist/types/modes/warp-events.test.d.ts +1 -0
- package/dist/types/plan-mode/model-transition.d.ts +47 -0
- package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
- package/dist/types/registry/agent-lifecycle.d.ts +26 -1
- package/dist/types/registry/persisted-agents.d.ts +3 -0
- package/dist/types/sdk.d.ts +27 -5
- package/dist/types/session/agent-session.d.ts +34 -10
- package/dist/types/session/session-entries.d.ts +6 -1
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-manager.d.ts +19 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
- package/dist/types/task/executor.d.ts +26 -1
- package/dist/types/task/index.d.ts +1 -1
- package/dist/types/task/label.d.ts +1 -1
- package/dist/types/task/parallel.d.ts +14 -0
- package/dist/types/task/structured-subagent.d.ts +111 -0
- package/dist/types/task/types.d.ts +51 -0
- package/dist/types/telemetry-export.d.ts +34 -9
- package/dist/types/tools/approval.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/essential-tools.d.ts +29 -0
- package/dist/types/tools/fetch.d.ts +4 -5
- package/dist/types/tools/hub/messaging.d.ts +5 -1
- package/dist/types/tools/image-gen.d.ts +2 -1
- package/dist/types/tools/index.d.ts +17 -1
- package/dist/types/tools/todo.d.ts +31 -0
- package/dist/types/tools/xdev.d.ts +11 -2
- package/dist/types/tui/tree-list.d.ts +7 -0
- package/dist/types/utils/markit.d.ts +11 -0
- package/dist/types/utils/title-generator.d.ts +2 -1
- package/dist/types/web/search/providers/kimi.d.ts +4 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +21 -16
- package/src/advisor/__tests__/advisor.test.ts +1304 -42
- package/src/advisor/__tests__/config.test.ts +58 -2
- package/src/advisor/config.ts +76 -24
- package/src/advisor/runtime.ts +445 -92
- package/src/autolearn/controller.ts +23 -28
- package/src/cli/file-processor.ts +1 -2
- package/src/cli.ts +5 -1
- package/src/config/model-discovery.ts +81 -21
- package/src/config/model-registry.ts +25 -6
- package/src/config/model-resolver.ts +14 -7
- package/src/config/settings-schema.ts +46 -9
- package/src/config/settings.ts +405 -25
- package/src/cursor.ts +20 -3
- package/src/debug/report-bundle.ts +40 -4
- package/src/discovery/helpers.ts +28 -5
- package/src/eval/__tests__/agent-bridge.test.ts +133 -47
- package/src/eval/__tests__/prelude-agent.test.ts +29 -0
- package/src/eval/agent-bridge.ts +104 -477
- package/src/eval/jl/prelude.jl +7 -6
- package/src/eval/js/shared/prelude.txt +5 -4
- package/src/eval/py/prelude.py +11 -39
- package/src/eval/rb/prelude.rb +5 -6
- package/src/exec/bash-executor.ts +14 -5
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/extensibility/extensions/loader.ts +3 -3
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/runner.ts +26 -0
- package/src/extensibility/extensions/types.ts +18 -0
- package/src/extensibility/extensions/wrapper.ts +2 -1
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +149 -8
- package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
- package/src/extensibility/plugins/manager.ts +2 -2
- package/src/extensibility/shared-events.ts +6 -0
- package/src/extensibility/utils.ts +91 -25
- package/src/irc/bus.ts +22 -3
- package/src/launch/broker.ts +3 -2
- package/src/launch/client.ts +2 -2
- package/src/launch/presence.ts +2 -2
- package/src/lsp/client.ts +58 -1
- package/src/lsp/index.ts +62 -6
- package/src/lsp/types.ts +3 -0
- package/src/main.ts +11 -8
- package/src/mcp/manager.ts +9 -3
- package/src/mcp/oauth-flow.ts +20 -0
- package/src/mcp/transports/stdio.test.ts +269 -1
- package/src/mcp/transports/stdio.ts +255 -24
- package/src/modes/components/advisor-config.ts +65 -3
- package/src/modes/components/agent-hub.ts +1 -72
- package/src/modes/components/ask-dialog.ts +1 -1
- package/src/modes/components/bash-execution.ts +7 -3
- package/src/modes/components/eval-execution.ts +3 -1
- package/src/modes/components/hook-editor.ts +18 -3
- package/src/modes/components/model-hub.ts +138 -42
- package/src/modes/components/session-selector.ts +4 -0
- package/src/modes/components/status-line/component.test.ts +1 -0
- package/src/modes/components/status-line/segments.ts +21 -6
- package/src/modes/controllers/command-controller.ts +167 -47
- package/src/modes/controllers/event-controller.ts +5 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -22
- package/src/modes/controllers/input-controller.ts +12 -12
- package/src/modes/controllers/selector-controller.ts +191 -31
- package/src/modes/interactive-mode.ts +169 -62
- package/src/modes/print-mode.ts +3 -3
- package/src/modes/rpc/host-tools.ts +2 -1
- package/src/modes/rpc/rpc-mode.ts +19 -4
- package/src/modes/warp-events.test.ts +794 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/plan-mode/model-transition.test.ts +60 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/tools/eval.md +5 -2
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/task.md +12 -8
- package/src/prompts/tools/write.md +1 -1
- package/src/registry/agent-lifecycle.ts +133 -18
- package/src/registry/persisted-agents.ts +74 -0
- package/src/sdk.ts +343 -122
- package/src/session/agent-session.ts +1359 -368
- package/src/session/session-entries.ts +6 -1
- package/src/session/session-history-format.ts +20 -5
- package/src/session/session-manager.ts +57 -0
- package/src/session/streaming-output.ts +41 -1
- package/src/slash-commands/builtin-registry.ts +7 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
- package/src/system-prompt.ts +7 -2
- package/src/task/executor.ts +100 -22
- package/src/task/index.ts +258 -429
- package/src/task/label.ts +2 -0
- package/src/task/parallel.ts +43 -0
- package/src/task/persisted-revive.ts +19 -7
- package/src/task/structured-subagent.ts +642 -0
- package/src/task/types.ts +58 -0
- package/src/telemetry-export.ts +453 -97
- package/src/tools/__tests__/eval-description.test.ts +1 -1
- package/src/tools/approval.ts +11 -0
- package/src/tools/bash.ts +71 -38
- package/src/tools/essential-tools.ts +45 -0
- package/src/tools/fetch.ts +28 -105
- package/src/tools/gh.ts +169 -2
- package/src/tools/hub/messaging.ts +16 -3
- package/src/tools/image-gen.ts +69 -7
- package/src/tools/index.ts +50 -15
- package/src/tools/path-utils.ts +1 -0
- package/src/tools/read.ts +62 -29
- package/src/tools/todo.ts +126 -13
- package/src/tools/write.ts +22 -4
- package/src/tools/xdev.ts +14 -3
- package/src/tui/tree-list.ts +39 -0
- package/src/utils/markit.ts +12 -0
- package/src/utils/title-generator.ts +15 -4
- package/src/utils/zip.ts +16 -1
- package/src/web/search/providers/kimi.ts +18 -12
- package/src/web/search/types.ts +6 -1
|
@@ -12,12 +12,18 @@
|
|
|
12
12
|
* the same module identity as a direct `@oh-my-pi/pi-coding-agent` import.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import
|
|
15
|
+
import { Database } from "bun:sqlite";
|
|
16
|
+
import * as fs from "node:fs";
|
|
16
17
|
import * as path from "node:path";
|
|
17
18
|
import type { AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
18
|
-
import type
|
|
19
|
+
import { type AuthCredential, SqliteAuthCredentialStore, type TSchema } from "@oh-my-pi/pi-ai";
|
|
19
20
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
getAgentDbPath,
|
|
23
|
+
getAgentDir,
|
|
24
|
+
getProjectDir,
|
|
25
|
+
parseFrontmatter as parseOmpFrontmatter,
|
|
26
|
+
} from "@oh-my-pi/pi-utils";
|
|
21
27
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
22
28
|
import { type SettingPath, Settings } from "../config/settings";
|
|
23
29
|
import { EditTool } from "../edit";
|
|
@@ -44,9 +50,10 @@ import { ReadTool } from "../tools/read";
|
|
|
44
50
|
import { formatBytes } from "../tools/render-utils";
|
|
45
51
|
import { WriteTool } from "../tools/write";
|
|
46
52
|
import { EventBus } from "../utils/event-bus";
|
|
47
|
-
import { loadExtensionFromFactory, loadExtensions } from "./extensions";
|
|
53
|
+
import { discoverExtensionPaths, loadExtensionFromFactory, loadExtensions } from "./extensions";
|
|
48
54
|
import { ExtensionRuntime } from "./extensions/loader";
|
|
49
55
|
import type { ExtensionFactory, ToolDefinition } from "./extensions/types";
|
|
56
|
+
import { getEnabledPlugins, resolvePluginExtensionPaths, type ScopedInstalledPlugin } from "./plugins/loader";
|
|
50
57
|
import type { Skill } from "./skills";
|
|
51
58
|
import { loadSkillsFromDir } from "./skills";
|
|
52
59
|
import { Type } from "./typebox";
|
|
@@ -605,16 +612,16 @@ export function createLsToolDefinition(cwd: string, options?: LsToolOptions): To
|
|
|
605
612
|
const ops = options?.operations;
|
|
606
613
|
const exists = ops
|
|
607
614
|
? await ops.exists(absolutePath)
|
|
608
|
-
: await fs.stat(absolutePath).then(
|
|
615
|
+
: await fs.promises.stat(absolutePath).then(
|
|
609
616
|
() => true,
|
|
610
617
|
() => false,
|
|
611
618
|
);
|
|
612
619
|
if (!exists) throw new Error(`Path not found: ${absolutePath}`);
|
|
613
|
-
const stat = ops ? await ops.stat(absolutePath) : await fs.stat(absolutePath);
|
|
620
|
+
const stat = ops ? await ops.stat(absolutePath) : await fs.promises.stat(absolutePath);
|
|
614
621
|
if (!stat.isDirectory()) {
|
|
615
622
|
return { content: [{ type: "text", text: rawPath }] };
|
|
616
623
|
}
|
|
617
|
-
const entries = ops ? await ops.readdir(absolutePath) : await fs.readdir(absolutePath);
|
|
624
|
+
const entries = ops ? await ops.readdir(absolutePath) : await fs.promises.readdir(absolutePath);
|
|
618
625
|
const sorted = [...entries].sort((a, b) => a.localeCompare(b));
|
|
619
626
|
const limited = sorted.slice(0, limit);
|
|
620
627
|
const output = limited.join("\n");
|
|
@@ -655,6 +662,100 @@ export const SettingsManager = {
|
|
|
655
662
|
},
|
|
656
663
|
} as const;
|
|
657
664
|
|
|
665
|
+
/** Scope used by the legacy package manager for discovered resources. */
|
|
666
|
+
export type SourceScope = "user" | "project" | "temporary";
|
|
667
|
+
|
|
668
|
+
/** Discovery metadata exposed alongside a legacy package resource path. */
|
|
669
|
+
export interface PathMetadata {
|
|
670
|
+
source: string;
|
|
671
|
+
scope: SourceScope;
|
|
672
|
+
origin: "package" | "top-level";
|
|
673
|
+
baseDir?: string;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/** One extension, skill, prompt, or theme resolved by the legacy package manager. */
|
|
677
|
+
export interface ResolvedResource {
|
|
678
|
+
path: string;
|
|
679
|
+
enabled: boolean;
|
|
680
|
+
metadata: PathMetadata;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** Resource groups returned by {@link DefaultPackageManager.resolve}. */
|
|
684
|
+
export interface ResolvedPaths {
|
|
685
|
+
extensions: ResolvedResource[];
|
|
686
|
+
skills: ResolvedResource[];
|
|
687
|
+
prompts: ResolvedResource[];
|
|
688
|
+
themes: ResolvedResource[];
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/** Action a legacy caller requests when a configured package is unavailable. */
|
|
692
|
+
export type MissingSourceAction = "install" | "skip" | "error";
|
|
693
|
+
|
|
694
|
+
/** Construction inputs accepted by the legacy package manager. */
|
|
695
|
+
export interface DefaultPackageManagerOptions {
|
|
696
|
+
cwd: string;
|
|
697
|
+
agentDir: string;
|
|
698
|
+
settingsManager: Settings | Promise<Settings>;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Enumerates the extensions OMP would load through the historical package
|
|
703
|
+
* manager surface used by legacy extensions.
|
|
704
|
+
*/
|
|
705
|
+
export class DefaultPackageManager {
|
|
706
|
+
#cwd: string;
|
|
707
|
+
#agentDir: string;
|
|
708
|
+
#settingsManager: Settings | Promise<Settings>;
|
|
709
|
+
|
|
710
|
+
constructor(options: DefaultPackageManagerOptions) {
|
|
711
|
+
this.#cwd = options.cwd;
|
|
712
|
+
this.#agentDir = options.agentDir;
|
|
713
|
+
this.#settingsManager = options.settingsManager;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/** Resolve enabled extension paths with their OMP plugin provenance. */
|
|
717
|
+
async resolve(_onMissing?: (source: string) => Promise<MissingSourceAction>): Promise<ResolvedPaths> {
|
|
718
|
+
const settings = await this.#settingsManager;
|
|
719
|
+
const configuredPaths = settings.get("extensions") ?? [];
|
|
720
|
+
const disabledExtensionIds = settings.get("disabledExtensions") ?? [];
|
|
721
|
+
const [extensionPaths, plugins] = await Promise.all([
|
|
722
|
+
discoverExtensionPaths(configuredPaths, this.#cwd, disabledExtensionIds),
|
|
723
|
+
getEnabledPlugins(this.#cwd),
|
|
724
|
+
]);
|
|
725
|
+
const pluginByExtensionPath = new Map<string, ScopedInstalledPlugin>();
|
|
726
|
+
for (const plugin of plugins) {
|
|
727
|
+
for (const extensionPath of resolvePluginExtensionPaths(plugin)) {
|
|
728
|
+
pluginByExtensionPath.set(path.resolve(extensionPath), plugin);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const extensions = extensionPaths.map(extensionPath => {
|
|
733
|
+
const resolvedPath = path.resolve(extensionPath);
|
|
734
|
+
const plugin = pluginByExtensionPath.get(resolvedPath);
|
|
735
|
+
const agentDirRelative = path.relative(path.resolve(this.#agentDir), resolvedPath);
|
|
736
|
+
const metadata: PathMetadata = plugin
|
|
737
|
+
? {
|
|
738
|
+
source: `npm:${plugin.name}`,
|
|
739
|
+
scope: plugin.scope,
|
|
740
|
+
origin: "package",
|
|
741
|
+
baseDir: plugin.path,
|
|
742
|
+
}
|
|
743
|
+
: {
|
|
744
|
+
source: "auto",
|
|
745
|
+
scope:
|
|
746
|
+
agentDirRelative === "" ||
|
|
747
|
+
(!agentDirRelative.startsWith("..") && !path.isAbsolute(agentDirRelative))
|
|
748
|
+
? "user"
|
|
749
|
+
: "project",
|
|
750
|
+
origin: "top-level",
|
|
751
|
+
};
|
|
752
|
+
return { path: resolvedPath, enabled: true, metadata };
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
return { extensions, skills: [], prompts: [], themes: [] };
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
658
759
|
/**
|
|
659
760
|
* Resource-loader compatibility layer for legacy pi extensions.
|
|
660
761
|
*
|
|
@@ -1005,7 +1106,7 @@ export class DefaultResourceLoader implements ResourceLoader {
|
|
|
1005
1106
|
: path.resolve(this.#state.cwd, resourcePath);
|
|
1006
1107
|
const files: string[] = [];
|
|
1007
1108
|
try {
|
|
1008
|
-
const stat = await fs.stat(resolvedPath);
|
|
1109
|
+
const stat = await fs.promises.stat(resolvedPath);
|
|
1009
1110
|
if (stat.isDirectory()) {
|
|
1010
1111
|
const glob = new Bun.Glob("**/*.md");
|
|
1011
1112
|
for await (const entry of glob.scan({ cwd: resolvedPath, absolute: false, onlyFiles: true })) {
|
|
@@ -1193,6 +1294,46 @@ export async function createAgentSession(
|
|
|
1193
1294
|
return ompCreateAgentSession(forwarded);
|
|
1194
1295
|
}
|
|
1195
1296
|
|
|
1297
|
+
/**
|
|
1298
|
+
* Synchronous auth storage surface retained for legacy extensions.
|
|
1299
|
+
*
|
|
1300
|
+
* Modern OMP auth storage is asynchronous, while older provider extensions
|
|
1301
|
+
* call `AuthStorage.create().get()` during module initialization.
|
|
1302
|
+
*/
|
|
1303
|
+
export class AuthStorage {
|
|
1304
|
+
constructor() {
|
|
1305
|
+
fs.mkdirSync(path.dirname(getAgentDbPath()), { recursive: true, mode: 0o700 });
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
static create(): AuthStorage {
|
|
1309
|
+
return new AuthStorage();
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
get(provider: string): AuthCredential | undefined {
|
|
1313
|
+
const store = new SqliteAuthCredentialStore(new Database(getAgentDbPath()));
|
|
1314
|
+
try {
|
|
1315
|
+
return store.listAuthCredentials(provider)[0]?.credential;
|
|
1316
|
+
} finally {
|
|
1317
|
+
store.close();
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
set(provider: string, credential: AuthCredential): void {
|
|
1322
|
+
const store = new SqliteAuthCredentialStore(new Database(getAgentDbPath()));
|
|
1323
|
+
try {
|
|
1324
|
+
store.upsertAuthCredentialForProvider(provider, credential);
|
|
1325
|
+
} finally {
|
|
1326
|
+
store.close();
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/** Read the first active credential for a legacy extension provider. */
|
|
1332
|
+
export function readStoredCredential(provider: string): AuthCredential | undefined {
|
|
1333
|
+
const storage = AuthStorage.create();
|
|
1334
|
+
return storage.get(provider);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1196
1337
|
export * from "../index";
|
|
1197
1338
|
export { formatBytes as formatSize } from "../tools/render-utils";
|
|
1198
1339
|
export { Type } from "./typebox";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference path="./legacy-pi-virtual-modules.d.ts" />
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
-
import { isBuiltin } from "node:module";
|
|
3
|
+
import { createRequire, isBuiltin } from "node:module";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import * as url from "node:url";
|
|
6
6
|
import { isCompiledBinary, stripWindowsExtendedLengthPathPrefix } from "@oh-my-pi/pi-utils";
|
|
@@ -1111,6 +1111,74 @@ const EXTENSION_GRAPH_SPECIFIER_REGEX = /((?:from\s+|import\s+|import\s*\(\s*)["
|
|
|
1111
1111
|
// reloads install supplemental hooks only for modules added to the graph since
|
|
1112
1112
|
// the previous load.
|
|
1113
1113
|
const extensionGraphHookModules = new Map<string, Set<string>>();
|
|
1114
|
+
const commonJsModuleSources = new Map<string, string>();
|
|
1115
|
+
const commonJsFallbackModulePaths = new Map<string, string>();
|
|
1116
|
+
const COMMONJS_REQUIRE_GLOBAL = "__ompLegacyPiRequireGraphModule";
|
|
1117
|
+
const commonJsModuleDefinitions = new Map<string, { source: string; filename: string; dirname: string }>();
|
|
1118
|
+
const commonJsModuleCache = new Map<
|
|
1119
|
+
string,
|
|
1120
|
+
{
|
|
1121
|
+
exports: unknown;
|
|
1122
|
+
filename: string;
|
|
1123
|
+
id: string;
|
|
1124
|
+
path: string;
|
|
1125
|
+
require: NodeJS.Require;
|
|
1126
|
+
loaded: boolean;
|
|
1127
|
+
}
|
|
1128
|
+
>();
|
|
1129
|
+
const commonJsTypeScriptTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
1130
|
+
|
|
1131
|
+
function evaluateGraphCommonJs(modulePath: string): unknown {
|
|
1132
|
+
const cached = commonJsModuleCache.get(modulePath);
|
|
1133
|
+
if (cached) {
|
|
1134
|
+
return cached.exports;
|
|
1135
|
+
}
|
|
1136
|
+
const definition = commonJsModuleDefinitions.get(modulePath);
|
|
1137
|
+
if (!definition) {
|
|
1138
|
+
throw new Error(`Missing graph-owned CommonJS definition: ${modulePath}`);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
const nativeRequire = createRequire(definition.filename);
|
|
1142
|
+
const module = {
|
|
1143
|
+
exports: {},
|
|
1144
|
+
filename: definition.filename,
|
|
1145
|
+
id: definition.filename,
|
|
1146
|
+
path: definition.dirname,
|
|
1147
|
+
require: nativeRequire,
|
|
1148
|
+
loaded: false,
|
|
1149
|
+
};
|
|
1150
|
+
commonJsModuleCache.set(modulePath, module);
|
|
1151
|
+
const graphRequire: NodeJS.Require = Object.assign(
|
|
1152
|
+
(specifier: string) => {
|
|
1153
|
+
const resolved = nativeRequire.resolve(specifier);
|
|
1154
|
+
let graphPath = resolved;
|
|
1155
|
+
try {
|
|
1156
|
+
graphPath = fs.realpathSync(resolved);
|
|
1157
|
+
} catch {
|
|
1158
|
+
// Builtins and virtual modules have no filesystem realpath.
|
|
1159
|
+
}
|
|
1160
|
+
return commonJsModuleDefinitions.has(graphPath) ? evaluateGraphCommonJs(graphPath) : nativeRequire(specifier);
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
resolve: nativeRequire.resolve,
|
|
1164
|
+
cache: nativeRequire.cache,
|
|
1165
|
+
extensions: nativeRequire.extensions,
|
|
1166
|
+
main: nativeRequire.main,
|
|
1167
|
+
},
|
|
1168
|
+
);
|
|
1169
|
+
module.require = graphRequire;
|
|
1170
|
+
const execute = new Function("exports", "require", "module", "__filename", "__dirname", definition.source);
|
|
1171
|
+
try {
|
|
1172
|
+
execute.call(module.exports, module.exports, graphRequire, module, definition.filename, definition.dirname);
|
|
1173
|
+
module.loaded = true;
|
|
1174
|
+
return module.exports;
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
commonJsModuleCache.delete(modulePath);
|
|
1177
|
+
throw error;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
Reflect.set(globalThis, COMMONJS_REQUIRE_GLOBAL, evaluateGraphCommonJs);
|
|
1114
1182
|
|
|
1115
1183
|
let legacyPiLoadTag = 0;
|
|
1116
1184
|
|
|
@@ -1144,9 +1212,9 @@ async function realpathOrSelfUncached(p: string): Promise<string> {
|
|
|
1144
1212
|
* Extension-local bare dependency entries are also included so their relative
|
|
1145
1213
|
* children receive the reload mtime tag; bare imports inside those dependencies
|
|
1146
1214
|
* remain native Bun resolutions to avoid taking over full third-party graphs.
|
|
1147
|
-
* CommonJS modules reached through `require()` stay on Bun's native loader
|
|
1148
|
-
*
|
|
1149
|
-
*
|
|
1215
|
+
* CommonJS modules reached through `require()` stay on Bun's native loader
|
|
1216
|
+
* unless they resolve native addons. CommonJS reached through ESM imports stays
|
|
1217
|
+
* graph-owned so the load hook can expose its exports through an ESM default.
|
|
1150
1218
|
*/
|
|
1151
1219
|
async function collectExtensionModules(entryRealPath: string): Promise<Map<string, string>> {
|
|
1152
1220
|
const modules = new Map<string, string>();
|
|
@@ -1253,21 +1321,106 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1253
1321
|
return modules;
|
|
1254
1322
|
}
|
|
1255
1323
|
|
|
1324
|
+
/**
|
|
1325
|
+
* Discovers CommonJS export names Bun normally exposes to ESM importers. The
|
|
1326
|
+
* bridge must declare them statically because its default export is synthetic.
|
|
1327
|
+
*/
|
|
1328
|
+
function collectCommonJsNamedExports(source: string): string[] {
|
|
1329
|
+
const names = new Set<string>();
|
|
1330
|
+
const assignmentPattern = /(?:^|[;\n])\s*(?:exports|module\.exports)\.([A-Za-z_$][\w$]*)\s*=/gm;
|
|
1331
|
+
for (const match of source.matchAll(assignmentPattern)) {
|
|
1332
|
+
const name = match[1];
|
|
1333
|
+
if (name && name !== "default") {
|
|
1334
|
+
names.add(name);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
const objectPattern = /module\.exports\s*=\s*\{([\s\S]*?)\}/g;
|
|
1338
|
+
for (const objectMatch of source.matchAll(objectPattern)) {
|
|
1339
|
+
const propertyPattern = /(?:^|,)\s*(?:([A-Za-z_$][\w$]*)\s*(?=[:,]|$)|["']([A-Za-z_$][\w$]*)["']\s*:)/g;
|
|
1340
|
+
for (const propertyMatch of objectMatch[1]?.matchAll(propertyPattern) ?? []) {
|
|
1341
|
+
const name = propertyMatch[1] ?? propertyMatch[2];
|
|
1342
|
+
if (name && name !== "default") {
|
|
1343
|
+
names.add(name);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
return [...names];
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* The shared evaluator gives ESM imports and sibling `require()` calls the
|
|
1352
|
+
* same `module.exports` value and cycle-aware cache.
|
|
1353
|
+
*/
|
|
1354
|
+
function synthesizeCommonJsDefaultModule(modulePath: string, source: string, targetPath = modulePath): string {
|
|
1355
|
+
let commonJsSource = source;
|
|
1356
|
+
if (commonJsSource.startsWith("#!")) {
|
|
1357
|
+
const firstLineEnd = commonJsSource.indexOf("\n");
|
|
1358
|
+
commonJsSource = firstLineEnd === -1 ? "" : commonJsSource.slice(firstLineEnd + 1);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const executableSource = targetPath.endsWith(".cts")
|
|
1362
|
+
? commonJsTypeScriptTranspiler.transformSync(commonJsSource)
|
|
1363
|
+
: commonJsSource;
|
|
1364
|
+
commonJsModuleDefinitions.set(modulePath, {
|
|
1365
|
+
source: executableSource,
|
|
1366
|
+
filename: targetPath,
|
|
1367
|
+
dirname: path.dirname(targetPath),
|
|
1368
|
+
});
|
|
1369
|
+
commonJsModuleCache.delete(modulePath);
|
|
1370
|
+
const exportsBinding = "__ompLegacyPiCommonJsExports";
|
|
1371
|
+
const namedExports = collectCommonJsNamedExports(executableSource)
|
|
1372
|
+
.map(
|
|
1373
|
+
(name, index) =>
|
|
1374
|
+
`const __ompLegacyPiCommonJsExport${index} = ${exportsBinding}[${JSON.stringify(name)}]; export { __ompLegacyPiCommonJsExport${index} as ${name} };`,
|
|
1375
|
+
)
|
|
1376
|
+
.join("\n");
|
|
1377
|
+
return `const ${exportsBinding} = globalThis[${JSON.stringify(COMMONJS_REQUIRE_GLOBAL)}](${JSON.stringify(modulePath)});\nexport default ${exportsBinding};\n${namedExports}\n`;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* Linkedom's canvas bridge uses its bundled fallback because OMP does not ship
|
|
1382
|
+
* native canvas.
|
|
1383
|
+
*/
|
|
1384
|
+
async function prepareCommonJsDefaultModule(modulePath: string, source: string): Promise<string> {
|
|
1385
|
+
const packageRoot = await findPackageRoot(modulePath);
|
|
1386
|
+
if (!packageRoot) {
|
|
1387
|
+
return synthesizeCommonJsDefaultModule(modulePath, source);
|
|
1388
|
+
}
|
|
1389
|
+
const manifest = await readPackageManifest(packageRoot);
|
|
1390
|
+
const packageRelativePath = path.relative(packageRoot, modulePath).split(path.sep).join("/");
|
|
1391
|
+
if (manifest?.name !== "linkedom" || packageRelativePath !== "commonjs/canvas.cjs") {
|
|
1392
|
+
return synthesizeCommonJsDefaultModule(modulePath, source);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
const targetPath = path.join(packageRoot, "commonjs", "canvas-shim.cjs");
|
|
1396
|
+
commonJsFallbackModulePaths.set(modulePath, targetPath);
|
|
1397
|
+
return synthesizeCommonJsDefaultModule(modulePath, await Bun.file(targetPath).text(), targetPath);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1256
1400
|
/**
|
|
1257
1401
|
* Install exact-path load hooks for the current extension graph. ESM/TS source
|
|
1258
|
-
* retains the async rewrite path.
|
|
1259
|
-
*
|
|
1260
|
-
* `
|
|
1402
|
+
* retains the async rewrite path. CommonJS wrappers and native-addon loaders
|
|
1403
|
+
* stay synchronous because Bun rejects `require()` targets backed by async
|
|
1404
|
+
* `onLoad` callbacks.
|
|
1261
1405
|
*/
|
|
1262
|
-
function installExtensionGraphHook(
|
|
1406
|
+
async function installExtensionGraphHook(
|
|
1263
1407
|
entryRealPath: string,
|
|
1264
1408
|
modules: Map<string, string>,
|
|
1265
|
-
|
|
1409
|
+
commonJsPaths: Set<string>,
|
|
1410
|
+
): Promise<{ asyncModules: Map<string, string>; syncSourceModules: Map<string, string> }> {
|
|
1266
1411
|
const asyncModules = new Map<string, string>();
|
|
1267
|
-
const
|
|
1412
|
+
const syncSourceModules = new Map<string, string>();
|
|
1268
1413
|
for (const [modulePath, source] of modules) {
|
|
1269
|
-
const
|
|
1270
|
-
|
|
1414
|
+
const extension = path.extname(modulePath);
|
|
1415
|
+
if (extension === ".cjs" || extension === ".cts") {
|
|
1416
|
+
if (!commonJsPaths.has(modulePath)) {
|
|
1417
|
+
throw new Error(`Missing CommonJS compatibility source: ${modulePath}`);
|
|
1418
|
+
}
|
|
1419
|
+
} else if (nativeAddonLoaderModulePaths.has(modulePath)) {
|
|
1420
|
+
syncSourceModules.set(modulePath, source);
|
|
1421
|
+
} else {
|
|
1422
|
+
asyncModules.set(modulePath, source);
|
|
1423
|
+
}
|
|
1271
1424
|
}
|
|
1272
1425
|
|
|
1273
1426
|
if (asyncModules.size > 0) {
|
|
@@ -1299,17 +1452,42 @@ function installExtensionGraphHook(
|
|
|
1299
1452
|
});
|
|
1300
1453
|
}
|
|
1301
1454
|
|
|
1302
|
-
if (
|
|
1303
|
-
const alternation = [...
|
|
1455
|
+
if (commonJsPaths.size > 0) {
|
|
1456
|
+
const alternation = [...commonJsPaths].map(escapeRegExp).join("|");
|
|
1457
|
+
const filter = new RegExp(`^(?:${alternation})(?:\\?mtime=\\d+)?$`);
|
|
1458
|
+
const hookId = Bun.hash(`${entryRealPath}\0commonjs\0${[...commonJsPaths].join("\0")}`).toString(36);
|
|
1459
|
+
Bun.plugin({
|
|
1460
|
+
name: `omp:legacy-pi-ext:${hookId}`,
|
|
1461
|
+
setup(build) {
|
|
1462
|
+
build.onLoad({ filter, namespace: "file" }, args => {
|
|
1463
|
+
const queryIndex = args.path.indexOf("?mtime=");
|
|
1464
|
+
const sourcePath = queryIndex >= 0 ? args.path.slice(0, queryIndex) : args.path;
|
|
1465
|
+
const source =
|
|
1466
|
+
commonJsModuleSources.get(sourcePath) ??
|
|
1467
|
+
synthesizeCommonJsDefaultModule(
|
|
1468
|
+
sourcePath,
|
|
1469
|
+
fs.readFileSync(commonJsFallbackModulePaths.get(sourcePath) ?? sourcePath, "utf8"),
|
|
1470
|
+
commonJsFallbackModulePaths.get(sourcePath) ?? sourcePath,
|
|
1471
|
+
);
|
|
1472
|
+
return { contents: source, loader: getLoader(sourcePath) };
|
|
1473
|
+
});
|
|
1474
|
+
},
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
if (syncSourceModules.size > 0) {
|
|
1479
|
+
const alternation = [...syncSourceModules.keys()].map(escapeRegExp).join("|");
|
|
1304
1480
|
const filter = new RegExp(`^(?:${alternation})(?:\\?mtime=\\d+)?$`);
|
|
1305
|
-
const hookId = Bun.hash(`${entryRealPath}\0sync-
|
|
1481
|
+
const hookId = Bun.hash(`${entryRealPath}\0sync-source\0${[...syncSourceModules.keys()].join("\0")}`).toString(
|
|
1482
|
+
36,
|
|
1483
|
+
);
|
|
1306
1484
|
Bun.plugin({
|
|
1307
1485
|
name: `omp:legacy-pi-ext:${hookId}`,
|
|
1308
1486
|
setup(build) {
|
|
1309
1487
|
build.onLoad({ filter, namespace: "file" }, args => {
|
|
1310
1488
|
const queryIndex = args.path.indexOf("?mtime=");
|
|
1311
1489
|
const sourcePath = queryIndex >= 0 ? args.path.slice(0, queryIndex) : args.path;
|
|
1312
|
-
const source =
|
|
1490
|
+
const source = syncSourceModules.get(sourcePath);
|
|
1313
1491
|
if (source === undefined) {
|
|
1314
1492
|
throw new Error(`Missing pre-rewritten CommonJS extension source: ${sourcePath}`);
|
|
1315
1493
|
}
|
|
@@ -1318,7 +1496,7 @@ function installExtensionGraphHook(
|
|
|
1318
1496
|
},
|
|
1319
1497
|
});
|
|
1320
1498
|
}
|
|
1321
|
-
return { asyncModules,
|
|
1499
|
+
return { asyncModules, syncSourceModules };
|
|
1322
1500
|
}
|
|
1323
1501
|
|
|
1324
1502
|
/**
|
|
@@ -1331,6 +1509,14 @@ function installExtensionGraphHook(
|
|
|
1331
1509
|
*/
|
|
1332
1510
|
async function ensureExtensionGraphHook(entryRealPath: string): Promise<{ clear(): void } | undefined> {
|
|
1333
1511
|
const currentModules = await collectExtensionModules(entryRealPath);
|
|
1512
|
+
const commonJsPaths = new Set<string>();
|
|
1513
|
+
for (const [modulePath, source] of currentModules) {
|
|
1514
|
+
const extension = path.extname(modulePath);
|
|
1515
|
+
if (extension === ".cjs" || extension === ".cts") {
|
|
1516
|
+
commonJsModuleSources.set(modulePath, await prepareCommonJsDefaultModule(modulePath, source));
|
|
1517
|
+
commonJsPaths.add(modulePath);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1334
1520
|
let hookedModules = extensionGraphHookModules.get(entryRealPath);
|
|
1335
1521
|
if (!hookedModules) {
|
|
1336
1522
|
hookedModules = new Set<string>();
|
|
@@ -1338,23 +1524,40 @@ async function ensureExtensionGraphHook(entryRealPath: string): Promise<{ clear(
|
|
|
1338
1524
|
}
|
|
1339
1525
|
|
|
1340
1526
|
const pendingModules = new Map<string, string>();
|
|
1527
|
+
const pendingCommonJsPaths = new Set<string>();
|
|
1341
1528
|
for (const [modulePath, source] of currentModules) {
|
|
1342
1529
|
if (!hookedModules.has(modulePath)) {
|
|
1343
1530
|
pendingModules.set(modulePath, source);
|
|
1531
|
+
if (commonJsPaths.has(modulePath)) {
|
|
1532
|
+
pendingCommonJsPaths.add(modulePath);
|
|
1533
|
+
}
|
|
1344
1534
|
}
|
|
1345
1535
|
}
|
|
1346
|
-
if (pendingModules.size === 0) {
|
|
1536
|
+
if (pendingModules.size === 0 && commonJsPaths.size === 0) {
|
|
1347
1537
|
return undefined;
|
|
1348
1538
|
}
|
|
1349
1539
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1540
|
+
let asyncModules = new Map<string, string>();
|
|
1541
|
+
let syncSourceModules = new Map<string, string>();
|
|
1542
|
+
if (pendingModules.size > 0) {
|
|
1543
|
+
({ asyncModules, syncSourceModules } = await installExtensionGraphHook(
|
|
1544
|
+
entryRealPath,
|
|
1545
|
+
pendingModules,
|
|
1546
|
+
pendingCommonJsPaths,
|
|
1547
|
+
));
|
|
1548
|
+
for (const modulePath of pendingModules.keys()) {
|
|
1549
|
+
hookedModules.add(modulePath);
|
|
1550
|
+
}
|
|
1353
1551
|
}
|
|
1354
1552
|
return {
|
|
1355
1553
|
clear() {
|
|
1356
1554
|
asyncModules.clear();
|
|
1357
|
-
|
|
1555
|
+
syncSourceModules.clear();
|
|
1556
|
+
for (const modulePath of commonJsPaths) {
|
|
1557
|
+
commonJsModuleSources.delete(modulePath);
|
|
1558
|
+
commonJsModuleDefinitions.delete(modulePath);
|
|
1559
|
+
commonJsModuleCache.delete(modulePath);
|
|
1560
|
+
}
|
|
1358
1561
|
},
|
|
1359
1562
|
};
|
|
1360
1563
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
isEnoent,
|
|
12
12
|
logger,
|
|
13
13
|
} from "@oh-my-pi/pi-utils";
|
|
14
|
-
import {
|
|
14
|
+
import { withHostGuard } from "../utils";
|
|
15
15
|
import { refreshBunGitCache } from "./bun-git-cache";
|
|
16
16
|
import { type GitSource, parseGitUrl } from "./git-url";
|
|
17
17
|
import { installLegacyPiSpecifierShim, loadLegacyPiModule } from "./legacy-pi-compat";
|
|
@@ -375,7 +375,7 @@ export class PluginManager {
|
|
|
375
375
|
installLegacyPiSpecifierShim();
|
|
376
376
|
for (const extensionPath of loadable) {
|
|
377
377
|
try {
|
|
378
|
-
const module = await
|
|
378
|
+
const module = await withHostGuard(() => loadLegacyPiModule(extensionPath));
|
|
379
379
|
if (!hasExtensionFactoryExport(module)) {
|
|
380
380
|
errors.push(`${extensionPath}: extension does not export a valid factory function`);
|
|
381
381
|
}
|
|
@@ -191,6 +191,12 @@ export interface AgentStartEvent {
|
|
|
191
191
|
export interface AgentEndEvent {
|
|
192
192
|
type: "agent_end";
|
|
193
193
|
messages: AgentMessage[];
|
|
194
|
+
/**
|
|
195
|
+
* When true, the session has already scheduled an automatic continuation
|
|
196
|
+
* (auto-retry, empty/unexpected-stop retry, etc.). Subscribers must not
|
|
197
|
+
* treat this as a user-visible terminal settle.
|
|
198
|
+
*/
|
|
199
|
+
willContinue?: boolean;
|
|
194
200
|
}
|
|
195
201
|
|
|
196
202
|
/** Fired at the start of each turn */
|