@oh-my-pi/pi-coding-agent 15.13.3 → 16.0.1
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 +155 -133
- package/dist/cli.js +621 -530
- package/dist/types/advisor/__tests__/advisor.test.d.ts +1 -0
- package/dist/types/advisor/advise-tool.d.ts +58 -0
- package/dist/types/advisor/index.d.ts +3 -0
- package/dist/types/advisor/runtime.d.ts +52 -0
- package/dist/types/advisor/watchdog.d.ts +5 -0
- package/dist/types/config/model-roles.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +66 -5
- package/dist/types/discovery/helpers.d.ts +7 -0
- package/dist/types/eval/__tests__/prelude-agent.test.d.ts +1 -0
- package/dist/types/extensibility/plugins/runtime-config.d.ts +3 -0
- package/dist/types/modes/components/advisor-message.d.ts +9 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +3 -1
- package/dist/types/modes/types.d.ts +8 -1
- package/dist/types/sdk.d.ts +3 -3
- package/dist/types/session/agent-session.d.ts +81 -2
- package/dist/types/session/session-history-format.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +4 -1
- package/dist/types/session/yield-queue.d.ts +2 -0
- package/dist/types/task/index.d.ts +21 -0
- package/dist/types/tools/github-cache.d.ts +5 -4
- package/dist/types/tools/job.d.ts +1 -0
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/report-tool-issue.d.ts +0 -1
- package/dist/types/web/search/index.d.ts +2 -2
- package/dist/types/web/search/provider.d.ts +2 -0
- package/package.json +13 -13
- package/src/advisor/__tests__/advisor.test.ts +586 -0
- package/src/advisor/advise-tool.ts +87 -0
- package/src/advisor/index.ts +3 -0
- package/src/advisor/runtime.ts +248 -0
- package/src/advisor/watchdog.ts +83 -0
- package/src/cli/args.ts +1 -0
- package/src/collab/host.ts +1 -1
- package/src/config/model-roles.ts +13 -1
- package/src/config/settings-schema.ts +65 -6
- package/src/discovery/claude-plugins.ts +3 -42
- package/src/discovery/github.ts +101 -6
- package/src/discovery/helpers.ts +11 -0
- package/src/eval/__tests__/prelude-agent.test.ts +73 -0
- package/src/eval/js/shared/prelude.txt +12 -3
- package/src/eval/py/prelude.py +26 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +289 -80
- package/src/extensibility/plugins/loader.ts +3 -2
- package/src/extensibility/plugins/manager.ts +4 -3
- package/src/extensibility/plugins/marketplace/fetcher.ts +32 -34
- package/src/extensibility/plugins/runtime-config.ts +9 -0
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/internal-urls/issue-pr-protocol.ts +8 -4
- package/src/main.ts +9 -1
- package/src/modes/acp/acp-agent.ts +3 -3
- package/src/modes/components/advisor-message.ts +99 -0
- package/src/modes/components/agent-hub.ts +7 -0
- package/src/modes/components/assistant-message.ts +86 -0
- package/src/modes/components/settings-defs.ts +7 -0
- package/src/modes/components/status-line/segments.ts +20 -7
- package/src/modes/components/tips.txt +1 -1
- package/src/modes/controllers/command-controller.ts +69 -2
- package/src/modes/controllers/extension-ui-controller.ts +4 -3
- package/src/modes/controllers/input-controller.ts +1 -0
- package/src/modes/controllers/selector-controller.ts +7 -0
- package/src/modes/interactive-mode.ts +59 -2
- package/src/modes/rpc/rpc-mode.ts +3 -3
- package/src/modes/runtime-init.ts +2 -1
- package/src/modes/types.ts +8 -1
- package/src/modes/utils/ui-helpers.ts +9 -0
- package/src/prompts/advisor/advise-tool.md +1 -0
- package/src/prompts/advisor/system.md +31 -0
- package/src/prompts/agents/designer.md +8 -0
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +4 -1
- package/src/prompts/tools/eval.md +13 -3
- package/src/prompts/tools/irc.md +1 -1
- package/src/sdk.ts +61 -14
- package/src/session/agent-session.ts +667 -13
- package/src/session/session-dump-format.ts +15 -131
- package/src/session/session-history-format.ts +30 -11
- package/src/session/session-manager.ts +3 -1
- package/src/session/yield-queue.ts +5 -1
- package/src/slash-commands/builtin-registry.ts +105 -4
- package/src/system-prompt.ts +1 -1
- package/src/task/executor.ts +5 -4
- package/src/task/index.ts +70 -9
- package/src/tools/github-cache.ts +32 -7
- package/src/tools/job.ts +14 -1
- package/src/tools/path-utils.ts +33 -2
- package/src/tools/report-tool-issue.ts +2 -7
- package/src/web/scrapers/docs-rs.ts +2 -3
- package/src/web/search/index.ts +2 -2
- package/src/web/search/provider.ts +14 -2
package/src/tools/job.ts
CHANGED
|
@@ -372,6 +372,7 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
372
372
|
interface JobRenderArgs {
|
|
373
373
|
poll?: string[];
|
|
374
374
|
cancel?: string[];
|
|
375
|
+
list?: boolean;
|
|
375
376
|
}
|
|
376
377
|
|
|
377
378
|
const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
@@ -433,6 +434,7 @@ function flattenStructuredPreview(text: string): string {
|
|
|
433
434
|
}
|
|
434
435
|
|
|
435
436
|
function describeTarget(args: JobRenderArgs | undefined): string {
|
|
437
|
+
if (args?.list) return "background jobs";
|
|
436
438
|
const poll = args?.poll ?? [];
|
|
437
439
|
const cancel = args?.cancel ?? [];
|
|
438
440
|
const parts: string[] = [];
|
|
@@ -460,7 +462,7 @@ export const jobToolRenderer = {
|
|
|
460
462
|
uiTheme: Theme,
|
|
461
463
|
args?: JobRenderArgs,
|
|
462
464
|
): Component {
|
|
463
|
-
|
|
465
|
+
let jobs = result.details?.jobs ?? [];
|
|
464
466
|
|
|
465
467
|
if (jobs.length === 0) {
|
|
466
468
|
const fallback = result.content?.find(c => c.type === "text")?.text || "No jobs to process";
|
|
@@ -468,6 +470,17 @@ export const jobToolRenderer = {
|
|
|
468
470
|
return new Text([header, formatEmptyMessage(fallback, uiTheme)].join("\n"), 0, 0);
|
|
469
471
|
}
|
|
470
472
|
|
|
473
|
+
const isPollCall = args
|
|
474
|
+
? !args.list && (!args.cancel || args.cancel.length === 0 || args.poll !== undefined)
|
|
475
|
+
: true;
|
|
476
|
+
|
|
477
|
+
if (!options.isPartial && isPollCall) {
|
|
478
|
+
jobs = jobs.filter(job => job.status !== "running");
|
|
479
|
+
if (jobs.length === 0) {
|
|
480
|
+
return new Text("", 0, 0);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
471
484
|
const counts = { completed: 0, failed: 0, cancelled: 0, running: 0 };
|
|
472
485
|
for (const job of jobs) counts[job.status]++;
|
|
473
486
|
|
package/src/tools/path-utils.ts
CHANGED
|
@@ -143,6 +143,37 @@ export function expandPath(filePath: string): string {
|
|
|
143
143
|
const normalized = stripFileUrl(normalizeUnicodeSpaces(normalizeAtPrefix(filePath)));
|
|
144
144
|
return expandTilde(normalized);
|
|
145
145
|
}
|
|
146
|
+
|
|
147
|
+
function isAsciiDriveLetter(value: string): boolean {
|
|
148
|
+
if (value.length !== 1) return false;
|
|
149
|
+
const code = value.charCodeAt(0);
|
|
150
|
+
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function windowsDriveAliasPath(filePath: string): string | undefined {
|
|
154
|
+
if (!filePath.startsWith("/")) return undefined;
|
|
155
|
+
const parts = filePath.split("/");
|
|
156
|
+
if (parts[0] !== "") return undefined;
|
|
157
|
+
|
|
158
|
+
let drive: string | undefined;
|
|
159
|
+
let tailStart = 2;
|
|
160
|
+
if (parts.length >= 2 && isAsciiDriveLetter(parts[1] ?? "")) {
|
|
161
|
+
drive = parts[1]!.toUpperCase();
|
|
162
|
+
} else if (parts.length >= 3 && (parts[1] ?? "").toLowerCase() === "mnt" && isAsciiDriveLetter(parts[2] ?? "")) {
|
|
163
|
+
drive = parts[2]!.toUpperCase();
|
|
164
|
+
tailStart = 3;
|
|
165
|
+
}
|
|
166
|
+
if (!drive) return undefined;
|
|
167
|
+
|
|
168
|
+
const tail = parts.slice(tailStart).filter(Boolean).join("\\");
|
|
169
|
+
return tail ? `${drive}:\\${tail}` : `${drive}:\\`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function normalizeWindowsDriveAliasPath(filePath: string, platform: NodeJS.Platform = process.platform): string {
|
|
173
|
+
if (platform !== "win32") return filePath;
|
|
174
|
+
return windowsDriveAliasPath(filePath) ?? filePath;
|
|
175
|
+
}
|
|
176
|
+
|
|
146
177
|
/**
|
|
147
178
|
* Inclusive line range describing one selector segment (e.g. `50-100`,
|
|
148
179
|
* `301-`, or `50+10`). `endLine` is `undefined` for open-ended ranges.
|
|
@@ -353,7 +384,7 @@ export function isInternalUrlPath(filePath: string): boolean {
|
|
|
353
384
|
*/
|
|
354
385
|
export function resolveToCwd(filePath: string, cwd: string): string {
|
|
355
386
|
const normalized = normalizeLocalScheme(filePath);
|
|
356
|
-
const expanded = expandPath(normalized);
|
|
387
|
+
const expanded = normalizeWindowsDriveAliasPath(expandPath(normalized));
|
|
357
388
|
const expandedAndNormalized = normalizeLocalScheme(expanded);
|
|
358
389
|
|
|
359
390
|
assertNotInternalUrl(expandedAndNormalized, normalized);
|
|
@@ -530,8 +561,8 @@ export async function splitDelimitedPathEntry(
|
|
|
530
561
|
}
|
|
531
562
|
|
|
532
563
|
return (
|
|
533
|
-
(await tryDelimitedPathSplit(normalizedEntry, cwd, splitter, "comma", false)) ??
|
|
534
564
|
(await tryDelimitedPathSplit(normalizedEntry, cwd, splitter, "semicolon", false)) ??
|
|
565
|
+
(await tryDelimitedPathSplit(normalizedEntry, cwd, splitter, "comma", false)) ??
|
|
535
566
|
(await tryDelimitedPathSplit(normalizedEntry, cwd, splitter, "whitespace", true)) ??
|
|
536
567
|
(await tryDelimitedPathSplit(normalizedEntry, cwd, splitter, "mixed", true))
|
|
537
568
|
);
|
|
@@ -20,10 +20,9 @@
|
|
|
20
20
|
* never blocked on the network and never throws.
|
|
21
21
|
*/
|
|
22
22
|
import { Database } from "bun:sqlite";
|
|
23
|
-
import path from "node:path";
|
|
24
23
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
25
24
|
import type { FetchImpl } from "@oh-my-pi/pi-ai";
|
|
26
|
-
import { $env, $flag,
|
|
25
|
+
import { $env, $flag, getAutoQaDbDir, getInstallId, logger, VERSION } from "@oh-my-pi/pi-utils";
|
|
27
26
|
import { z } from "zod/v4";
|
|
28
27
|
import type { Settings } from "..";
|
|
29
28
|
import type { ToolSession } from "./index";
|
|
@@ -184,10 +183,6 @@ export async function resolveAutoQaConsent(settings: Settings | undefined): Prom
|
|
|
184
183
|
return consentInFlight;
|
|
185
184
|
}
|
|
186
185
|
|
|
187
|
-
export function getAutoQaDbPath(): string {
|
|
188
|
-
return path.join(getAgentDir(), "autoqa.db");
|
|
189
|
-
}
|
|
190
|
-
|
|
191
186
|
let cachedDb: Database | null = null;
|
|
192
187
|
|
|
193
188
|
/**
|
|
@@ -205,7 +200,7 @@ let cachedDb: Database | null = null;
|
|
|
205
200
|
export function openAutoQaDb(): Database | null {
|
|
206
201
|
if (cachedDb) return cachedDb;
|
|
207
202
|
try {
|
|
208
|
-
const db = new Database(
|
|
203
|
+
const db = new Database(getAutoQaDbDir());
|
|
209
204
|
// Install the busy handler BEFORE any lock-taking statement. See #2421.
|
|
210
205
|
db.run("PRAGMA busy_timeout = 5000");
|
|
211
206
|
db.run(`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { gunzipSync } from "node:zlib";
|
|
4
|
-
import {
|
|
4
|
+
import { getDocsRsCacheDir, isEnoent, logger, ptree, tryParseJson } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { ToolAbortError } from "../../tools/tool-errors";
|
|
6
6
|
import type { RenderResult, SpecialHandler } from "./types";
|
|
7
7
|
import { buildResult, MAX_BYTES } from "./types";
|
|
@@ -276,7 +276,6 @@ function findItemInModule(mod_: RustdocItem, name: string, index: Record<string,
|
|
|
276
276
|
return null;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
const DOCS_RS_CACHE_ROOT = "webcache";
|
|
280
279
|
const DOCS_RS_CACHE_FILENAME = "rustdoc.json";
|
|
281
280
|
|
|
282
281
|
function sanitizeCacheSegment(value: string): string {
|
|
@@ -291,7 +290,7 @@ function getDocsRsCacheVersionSegment(version: string, now = new Date()): string
|
|
|
291
290
|
function getDocsRsCachePath(target: DocsRsTarget, now = new Date()): string {
|
|
292
291
|
const crate = sanitizeCacheSegment(target.crateName);
|
|
293
292
|
const version = getDocsRsCacheVersionSegment(target.version, now);
|
|
294
|
-
return path.join(
|
|
293
|
+
return path.join(getDocsRsCacheDir(), `docsrs_${crate}_${version}`, DOCS_RS_CACHE_FILENAME);
|
|
295
294
|
}
|
|
296
295
|
|
|
297
296
|
async function readCachedRustdocCrate(
|
package/src/web/search/index.ts
CHANGED
|
@@ -300,6 +300,6 @@ export function getSearchTools(): CustomTool<any, any>[] {
|
|
|
300
300
|
return [webSearchCustomTool];
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
export { getSearchProvider, setPreferredSearchProvider } from "./provider";
|
|
303
|
+
export { getSearchProvider, setExcludedSearchProviders, setPreferredSearchProvider } from "./provider";
|
|
304
304
|
export type { SearchProviderId as SearchProvider, SearchResponse } from "./types";
|
|
305
|
-
export { isSearchProviderPreference } from "./types";
|
|
305
|
+
export { isSearchProviderId, isSearchProviderPreference } from "./types";
|
|
@@ -127,6 +127,18 @@ export function setPreferredSearchProvider(provider: SearchProviderId | "auto"):
|
|
|
127
127
|
preferredProvId = provider;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/** Providers excluded from web search resolution via settings. */
|
|
131
|
+
let excludedProvIds = new Set<SearchProviderId>();
|
|
132
|
+
|
|
133
|
+
/** Set providers that web search should never use, including fallbacks. */
|
|
134
|
+
export function setExcludedSearchProviders(providers: readonly SearchProviderId[]): void {
|
|
135
|
+
excludedProvIds = new Set(providers);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isSearchProviderExcluded(id: SearchProviderId): boolean {
|
|
139
|
+
return excludedProvIds.has(id);
|
|
140
|
+
}
|
|
141
|
+
|
|
130
142
|
/**
|
|
131
143
|
* Determine which providers are configured and currently available.
|
|
132
144
|
* Each candidate is loaded (and its `isAvailable()` called) only as the chain
|
|
@@ -138,7 +150,7 @@ export async function resolveProviderChain(
|
|
|
138
150
|
): Promise<SearchProvider[]> {
|
|
139
151
|
const providers: SearchProvider[] = [];
|
|
140
152
|
|
|
141
|
-
if (preferredProvider !== "auto") {
|
|
153
|
+
if (preferredProvider !== "auto" && !isSearchProviderExcluded(preferredProvider)) {
|
|
142
154
|
const provider = await getSearchProvider(preferredProvider);
|
|
143
155
|
if (await provider.isExplicitlyAvailable(authStorage)) {
|
|
144
156
|
providers.push(provider);
|
|
@@ -146,7 +158,7 @@ export async function resolveProviderChain(
|
|
|
146
158
|
}
|
|
147
159
|
|
|
148
160
|
for (const id of SEARCH_PROVIDER_ORDER) {
|
|
149
|
-
if (id === preferredProvider) continue;
|
|
161
|
+
if (id === preferredProvider || isSearchProviderExcluded(id)) continue;
|
|
150
162
|
const provider = await getSearchProvider(id);
|
|
151
163
|
if (await provider.isAvailable(authStorage)) {
|
|
152
164
|
providers.push(provider);
|