@oh-my-pi/pi-coding-agent 16.3.11 → 16.3.12
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 +49 -0
- package/dist/cli.js +3119 -3028
- package/dist/types/advisor/runtime.d.ts +11 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/settings-schema.d.ts +6 -0
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/discovery/helpers.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/__tests__/agent-protocol-nested.test.d.ts +1 -0
- package/dist/types/internal-urls/registry-helpers.d.ts +7 -5
- package/dist/types/modes/components/model-selector.d.ts +2 -1
- package/dist/types/modes/github-ref-autocomplete.d.ts +35 -0
- package/dist/types/modes/utils/context-usage.d.ts +0 -12
- package/dist/types/modes/workflow.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +5 -0
- package/dist/types/system-prompt.d.ts +1 -1
- package/dist/types/tools/bash-interactive.d.ts +1 -1
- package/dist/types/tools/bash-skill-urls.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +2 -1
- package/dist/types/tools/browser/launch.d.ts +1 -0
- package/dist/types/tools/grep.d.ts +2 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/path-utils.d.ts +24 -0
- package/dist/types/tools/read.d.ts +2 -0
- package/dist/types/utils/local-date.d.ts +2 -0
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +145 -0
- package/src/advisor/runtime.ts +19 -0
- package/src/config/api-key-resolver.ts +7 -2
- package/src/config/model-registry.ts +9 -0
- package/src/config/settings-schema.ts +11 -1
- package/src/config/settings.ts +11 -0
- package/src/discovery/builtin.ts +2 -1
- package/src/discovery/claude-plugins.ts +167 -46
- package/src/discovery/helpers.ts +16 -1
- package/src/edit/renderer.ts +20 -6
- package/src/eval/js/worker-core.ts +163 -6
- package/src/exec/bash-executor.ts +14 -9
- package/src/extensibility/plugins/legacy-pi-compat.ts +6 -2
- package/src/extensibility/plugins/marketplace/fetcher.ts +15 -14
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +68 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/registry-helpers.ts +9 -6
- package/src/modes/components/model-selector.ts +30 -6
- package/src/modes/components/settings-defs.ts +1 -1
- package/src/modes/components/status-line/component.ts +14 -2
- package/src/modes/controllers/command-controller.ts +13 -23
- package/src/modes/controllers/event-controller.ts +12 -12
- package/src/modes/controllers/extension-ui-controller.ts +6 -35
- package/src/modes/controllers/input-controller.ts +18 -2
- package/src/modes/controllers/mcp-command-controller.ts +10 -9
- package/src/modes/controllers/selector-controller.ts +16 -5
- package/src/modes/github-ref-autocomplete.ts +75 -0
- package/src/modes/interactive-mode.ts +54 -10
- package/src/modes/prompt-action-autocomplete.ts +35 -0
- package/src/modes/utils/context-usage.ts +58 -5
- package/src/modes/utils/hotkeys-markdown.ts +2 -1
- package/src/modes/utils/ui-helpers.ts +2 -2
- package/src/modes/workflow.ts +14 -8
- package/src/prompts/system/plan-mode-active.md +5 -2
- package/src/prompts/system/system-prompt.md +1 -2
- package/src/prompts/system/workflow-notice.md +69 -50
- package/src/prompts/tools/bash.md +18 -7
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/read.md +4 -3
- package/src/sdk.ts +11 -0
- package/src/session/agent-session.ts +128 -14
- package/src/system-prompt.ts +3 -2
- package/src/tools/bash-interactive.ts +1 -1
- package/src/tools/bash-skill-urls.ts +39 -7
- package/src/tools/bash.ts +69 -39
- package/src/tools/browser/launch.ts +31 -4
- package/src/tools/grep.ts +53 -14
- package/src/tools/index.ts +11 -0
- package/src/tools/path-utils.ts +46 -1
- package/src/tools/read.ts +108 -50
- package/src/utils/local-date.ts +7 -0
- package/src/utils/open.ts +36 -10
|
@@ -14,6 +14,7 @@ import { buildNonInteractiveEnv } from "./non-interactive-env";
|
|
|
14
14
|
|
|
15
15
|
export interface BashExecutorOptions {
|
|
16
16
|
cwd?: string;
|
|
17
|
+
/** Milliseconds before aborting the command; 0 disables the executor deadline. */
|
|
17
18
|
timeout?: number;
|
|
18
19
|
onChunk?: (chunk: string) => void;
|
|
19
20
|
chunkThrottleMs?: number;
|
|
@@ -296,11 +297,15 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
296
297
|
|
|
297
298
|
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
298
299
|
const timeoutDeferred = Promise.withResolvers<"timeout">();
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
const requestedTimeoutMs = options?.timeout;
|
|
301
|
+
const deadlineTimeoutMs = requestedTimeoutMs === 0 ? undefined : Math.max(1_000, requestedTimeoutMs ?? 300_000);
|
|
302
|
+
const nativeTimeoutMs = requestedTimeoutMs !== undefined && requestedTimeoutMs > 0 ? requestedTimeoutMs : undefined;
|
|
303
|
+
if (deadlineTimeoutMs !== undefined) {
|
|
304
|
+
timeoutTimer = setTimeout(() => {
|
|
305
|
+
abortCurrentExecution();
|
|
306
|
+
timeoutDeferred.resolve("timeout");
|
|
307
|
+
}, deadlineTimeoutMs);
|
|
308
|
+
}
|
|
304
309
|
|
|
305
310
|
let resetSession = false;
|
|
306
311
|
|
|
@@ -311,7 +316,7 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
311
316
|
command: finalCommand,
|
|
312
317
|
cwd: commandCwd,
|
|
313
318
|
env: commandEnv,
|
|
314
|
-
timeoutMs:
|
|
319
|
+
timeoutMs: nativeTimeoutMs,
|
|
315
320
|
signal: runAbortController.signal,
|
|
316
321
|
},
|
|
317
322
|
(err, chunk) => {
|
|
@@ -328,7 +333,7 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
328
333
|
sessionEnv: shellEnv,
|
|
329
334
|
snapshotPath: snapshotPath ?? undefined,
|
|
330
335
|
minimizer,
|
|
331
|
-
timeoutMs:
|
|
336
|
+
timeoutMs: nativeTimeoutMs,
|
|
332
337
|
signal: runAbortController.signal,
|
|
333
338
|
},
|
|
334
339
|
(err, chunk) => {
|
|
@@ -359,8 +364,8 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
359
364
|
exitCode: undefined,
|
|
360
365
|
cancelled: true,
|
|
361
366
|
...(await sink.dump(
|
|
362
|
-
winner.kind === "timeout"
|
|
363
|
-
? `Command timed out after ${Math.round(
|
|
367
|
+
winner.kind === "timeout" && deadlineTimeoutMs !== undefined
|
|
368
|
+
? `Command timed out after ${Math.round(deadlineTimeoutMs / 1000)} seconds`
|
|
364
369
|
: "Command cancelled",
|
|
365
370
|
)),
|
|
366
371
|
};
|
|
@@ -1059,7 +1059,8 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1059
1059
|
let resolved: string | null = null;
|
|
1060
1060
|
let nextFollowsBareDependencies = followBareDependencies;
|
|
1061
1061
|
if (specifier.startsWith(".")) {
|
|
1062
|
-
|
|
1062
|
+
const candidate = Bun.resolveSync(specifier, dir);
|
|
1063
|
+
resolved = hasSourceModuleExtension(candidate) ? await realpathOrSelf(candidate) : null;
|
|
1063
1064
|
} else if (specifier.startsWith("#")) {
|
|
1064
1065
|
resolved = await resolvePackageImportSpecifier(specifier, file);
|
|
1065
1066
|
} else if (
|
|
@@ -1078,7 +1079,10 @@ async function collectExtensionModules(entryRealPath: string): Promise<Map<strin
|
|
|
1078
1079
|
dependencyExtension === ".cjs" ||
|
|
1079
1080
|
dependencyExtension === ".cts" ||
|
|
1080
1081
|
((dependencyExtension === ".js" || dependencyExtension === ".jsx") && manifest?.type !== "module");
|
|
1081
|
-
resolved =
|
|
1082
|
+
resolved =
|
|
1083
|
+
dependencyEntry && hasSourceModuleExtension(dependencyEntry) && !isCommonJsEntry
|
|
1084
|
+
? await realpathOrSelf(dependencyEntry)
|
|
1085
|
+
: null;
|
|
1082
1086
|
nextFollowsBareDependencies = false;
|
|
1083
1087
|
}
|
|
1084
1088
|
if (resolved && !modules.has(resolved)) {
|
|
@@ -196,19 +196,20 @@ export function parseMarketplaceCatalog(content: string, filePath: string): Mark
|
|
|
196
196
|
* Catalog paths tried in priority order: omp-namespaced override first, then
|
|
197
197
|
* the Claude Code-compatible fallback so existing marketplaces keep loading.
|
|
198
198
|
*/
|
|
199
|
-
const CATALOG_RELATIVE_PATHS: readonly string[] = [
|
|
200
|
-
path.join(".omp-plugin", "marketplace.json"),
|
|
201
|
-
path.join(".claude-plugin", "marketplace.json"),
|
|
202
|
-
];
|
|
199
|
+
const CATALOG_RELATIVE_PATHS: readonly string[] = [".omp-plugin/marketplace.json", ".claude-plugin/marketplace.json"];
|
|
203
200
|
|
|
204
|
-
async function readMarketplaceCatalog(
|
|
201
|
+
async function readMarketplaceCatalog(
|
|
202
|
+
root: string,
|
|
203
|
+
options: { relativeDisplayPaths?: boolean } = {},
|
|
204
|
+
): Promise<{ catalogPath: string; displayPath: string; content: string }> {
|
|
205
205
|
const tried: string[] = [];
|
|
206
206
|
for (const rel of CATALOG_RELATIVE_PATHS) {
|
|
207
|
-
const catalogPath = path.join(root, rel);
|
|
208
|
-
|
|
207
|
+
const catalogPath = path.join(root, ...rel.split("/"));
|
|
208
|
+
const displayPath = options.relativeDisplayPaths ? rel : catalogPath;
|
|
209
|
+
tried.push(displayPath);
|
|
209
210
|
try {
|
|
210
211
|
const content = await Bun.file(catalogPath).text();
|
|
211
|
-
return { catalogPath, content };
|
|
212
|
+
return { catalogPath, displayPath, content };
|
|
212
213
|
} catch (err) {
|
|
213
214
|
if (isEnoent(err)) continue;
|
|
214
215
|
throw err;
|
|
@@ -252,11 +253,11 @@ export async function fetchMarketplace(source: string, cacheDir: string): Promis
|
|
|
252
253
|
|
|
253
254
|
if (type === "github") {
|
|
254
255
|
const url = `https://github.com/${source}.git`;
|
|
255
|
-
return cloneAndReadCatalog(url, cacheDir);
|
|
256
|
+
return cloneAndReadCatalog(url, source, cacheDir);
|
|
256
257
|
}
|
|
257
258
|
|
|
258
259
|
if (type === "git") {
|
|
259
|
-
return cloneAndReadCatalog(source, cacheDir);
|
|
260
|
+
return cloneAndReadCatalog(source, source, cacheDir);
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
// type === "url"
|
|
@@ -284,7 +285,7 @@ export async function fetchMarketplace(source: string, cacheDir: string): Promis
|
|
|
284
285
|
* responsible for promoting the clone to its final cache location via
|
|
285
286
|
* `promoteCloneToCache` after any duplicate/drift checks pass.
|
|
286
287
|
*/
|
|
287
|
-
async function cloneAndReadCatalog(url: string, cacheDir: string): Promise<FetchResult> {
|
|
288
|
+
async function cloneAndReadCatalog(url: string, source: string, cacheDir: string): Promise<FetchResult> {
|
|
288
289
|
const tmpDir = path.join(cacheDir, `.tmp-clone-${Date.now()}`);
|
|
289
290
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
290
291
|
|
|
@@ -292,12 +293,12 @@ async function cloneAndReadCatalog(url: string, cacheDir: string): Promise<Fetch
|
|
|
292
293
|
await git.clone(url, tmpDir);
|
|
293
294
|
|
|
294
295
|
try {
|
|
295
|
-
const {
|
|
296
|
-
const catalog = parseMarketplaceCatalog(content,
|
|
296
|
+
const { displayPath, content } = await readMarketplaceCatalog(tmpDir, { relativeDisplayPaths: true });
|
|
297
|
+
const catalog = parseMarketplaceCatalog(content, displayPath);
|
|
297
298
|
return { catalog, clonePath: tmpDir };
|
|
298
299
|
} catch (err) {
|
|
299
300
|
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
|
|
300
|
-
throw new Error(`Cloned repository ${url}: ${(err as Error).message}`, { cause: err });
|
|
301
|
+
throw new Error(`Cloned repository ${url}: ${(err as Error).message} (source: ${source})`, { cause: err });
|
|
301
302
|
}
|
|
302
303
|
}
|
|
303
304
|
|
|
@@ -33,7 +33,7 @@ export interface SessionStartEvent {
|
|
|
33
33
|
export interface SessionBeforeSwitchEvent {
|
|
34
34
|
type: "session_before_switch";
|
|
35
35
|
/** Reason for the switch */
|
|
36
|
-
reason: "new" | "resume" | "fork";
|
|
36
|
+
reason: "new" | "resume" | "fork" | "handoff";
|
|
37
37
|
/** Session file we're switching to (only for "resume") */
|
|
38
38
|
targetSessionFile?: string;
|
|
39
39
|
}
|
|
@@ -42,7 +42,7 @@ export interface SessionBeforeSwitchEvent {
|
|
|
42
42
|
export interface SessionSwitchEvent {
|
|
43
43
|
type: "session_switch";
|
|
44
44
|
/** Reason for the switch */
|
|
45
|
-
reason: "new" | "resume" | "fork";
|
|
45
|
+
reason: "new" | "resume" | "fork" | "handoff";
|
|
46
46
|
/** Session file we came from */
|
|
47
47
|
previousSessionFile: string | undefined;
|
|
48
48
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { afterAll, afterEach, expect, it } from "bun:test";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { TempDir } from "@oh-my-pi/pi-utils";
|
|
5
|
+
import { AgentRegistry } from "../../registry/agent-registry";
|
|
6
|
+
import type { AgentSession } from "../../session/agent-session";
|
|
7
|
+
import { ArtifactManager } from "../../session/artifacts";
|
|
8
|
+
import { AgentProtocolHandler } from "../agent-protocol";
|
|
9
|
+
import { resetRegisteredArtifactDirsForTests } from "../registry-helpers";
|
|
10
|
+
|
|
11
|
+
const tempDir = TempDir.createSync("omp-nested-agent-repro-");
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
AgentRegistry.resetGlobalForTests();
|
|
14
|
+
resetRegisteredArtifactDirsForTests();
|
|
15
|
+
});
|
|
16
|
+
afterAll(() => {
|
|
17
|
+
tempDir.removeSync();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("agent:// resolves a depth-2 subagent's .md output while its session is live and artifact-manager-adopted", async () => {
|
|
21
|
+
const root = tempDir.path();
|
|
22
|
+
const rootSessionFile = path.join(root, "session.jsonl");
|
|
23
|
+
const rootArtifactsDir = rootSessionFile.slice(0, -6);
|
|
24
|
+
await fs.mkdir(rootArtifactsDir, { recursive: true });
|
|
25
|
+
// Every subagent adopts the root ArtifactManager and reports its dir.
|
|
26
|
+
const sharedArtifactManager = new ArtifactManager(rootArtifactsDir);
|
|
27
|
+
|
|
28
|
+
// A depth-1 subagent's OWN children are written under its own
|
|
29
|
+
// sessionFile.slice(0, -6) (task/index.ts), i.e. one level deeper.
|
|
30
|
+
const midSessionFile = path.join(rootArtifactsDir, "CodexDeepDive.jsonl");
|
|
31
|
+
const midOwnArtifactsDir = midSessionFile.slice(0, -6);
|
|
32
|
+
await fs.mkdir(midOwnArtifactsDir, { recursive: true });
|
|
33
|
+
|
|
34
|
+
const grandchildId = "CodexDeepDive.GraphStore";
|
|
35
|
+
const grandchildSessionFile = path.join(midOwnArtifactsDir, `${grandchildId}.jsonl`);
|
|
36
|
+
await fs.writeFile(path.join(midOwnArtifactsDir, `${grandchildId}.md`), "full report content");
|
|
37
|
+
|
|
38
|
+
const fakeSession = {
|
|
39
|
+
sessionManager: { getArtifactsDir: () => sharedArtifactManager.dir },
|
|
40
|
+
} as unknown as AgentSession;
|
|
41
|
+
const registry = AgentRegistry.global();
|
|
42
|
+
registry.register({
|
|
43
|
+
id: "Main",
|
|
44
|
+
displayName: "main",
|
|
45
|
+
kind: "main",
|
|
46
|
+
session: fakeSession,
|
|
47
|
+
sessionFile: rootSessionFile,
|
|
48
|
+
});
|
|
49
|
+
registry.register({
|
|
50
|
+
id: "CodexDeepDive",
|
|
51
|
+
displayName: "sub",
|
|
52
|
+
kind: "sub",
|
|
53
|
+
parentId: "Main",
|
|
54
|
+
session: fakeSession,
|
|
55
|
+
sessionFile: midSessionFile,
|
|
56
|
+
});
|
|
57
|
+
registry.register({
|
|
58
|
+
id: grandchildId,
|
|
59
|
+
displayName: "sub",
|
|
60
|
+
kind: "sub",
|
|
61
|
+
parentId: "CodexDeepDive",
|
|
62
|
+
session: fakeSession,
|
|
63
|
+
sessionFile: grandchildSessionFile,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const resource = await new AgentProtocolHandler().resolve(new URL(`agent://${grandchildId}`) as never);
|
|
67
|
+
expect(resource.content).toBe("full report content");
|
|
68
|
+
});
|