@oh-my-pi/pi-coding-agent 14.0.5 → 14.1.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 +120 -0
- package/package.json +8 -8
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +43 -10
- package/src/async/support.ts +5 -0
- package/src/cli/list-models.ts +96 -57
- package/src/commit/agentic/tools/analyze-file.ts +1 -2
- package/src/commit/model-selection.ts +16 -13
- package/src/config/mcp-schema.json +1 -1
- package/src/config/model-equivalence.ts +675 -0
- package/src/config/model-registry.ts +242 -45
- package/src/config/model-resolver.ts +282 -65
- package/src/config/settings-schema.ts +27 -3
- package/src/config/settings.ts +1 -1
- package/src/cursor.ts +64 -23
- package/src/edit/index.ts +254 -89
- package/src/edit/modes/chunk.ts +336 -57
- package/src/edit/modes/hashline.ts +51 -26
- package/src/edit/modes/patch.ts +16 -10
- package/src/edit/modes/replace.ts +15 -7
- package/src/edit/renderer.ts +248 -94
- package/src/export/html/template.css +82 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +614 -97
- package/src/extensibility/custom-tools/types.ts +0 -3
- package/src/extensibility/extensions/loader.ts +16 -0
- package/src/extensibility/extensions/runner.ts +2 -7
- package/src/extensibility/extensions/types.ts +8 -4
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/internal-urls/jobs-protocol.ts +2 -1
- package/src/ipy/executor.ts +447 -52
- package/src/ipy/kernel.ts +39 -13
- package/src/lsp/client.ts +55 -1
- package/src/lsp/index.ts +8 -0
- package/src/lsp/types.ts +6 -0
- package/src/main.ts +6 -2
- package/src/memories/index.ts +7 -6
- package/src/modes/acp/acp-agent.ts +4 -1
- package/src/modes/components/bash-execution.ts +16 -4
- package/src/modes/components/model-selector.ts +221 -64
- package/src/modes/components/status-line/presets.ts +17 -6
- package/src/modes/components/status-line/segments.ts +15 -0
- package/src/modes/components/status-line-segment-editor.ts +1 -0
- package/src/modes/components/status-line.ts +7 -1
- package/src/modes/components/tool-execution.ts +145 -75
- package/src/modes/controllers/command-controller.ts +42 -1
- package/src/modes/controllers/event-controller.ts +4 -1
- package/src/modes/controllers/extension-ui-controller.ts +28 -5
- package/src/modes/controllers/input-controller.ts +9 -3
- package/src/modes/controllers/selector-controller.ts +17 -6
- package/src/modes/interactive-mode.ts +19 -3
- package/src/modes/print-mode.ts +13 -4
- package/src/modes/prompt-action-autocomplete.ts +3 -5
- package/src/modes/rpc/rpc-mode.ts +8 -2
- package/src/modes/shared.ts +2 -2
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/system/system-prompt.md +5 -1
- package/src/prompts/tools/bash.md +16 -1
- package/src/prompts/tools/cancel-job.md +1 -1
- package/src/prompts/tools/chunk-edit.md +191 -163
- package/src/prompts/tools/hashline.md +11 -11
- package/src/prompts/tools/patch.md +10 -5
- package/src/prompts/tools/{await.md → poll.md} +1 -1
- package/src/prompts/tools/read-chunk.md +12 -3
- package/src/prompts/tools/read.md +9 -0
- package/src/prompts/tools/task.md +2 -2
- package/src/prompts/tools/vim.md +98 -0
- package/src/prompts/tools/write.md +1 -0
- package/src/sdk.ts +758 -725
- package/src/session/agent-session.ts +187 -40
- package/src/session/session-manager.ts +50 -4
- package/src/slash-commands/builtin-registry.ts +17 -0
- package/src/task/executor.ts +9 -5
- package/src/task/index.ts +3 -5
- package/src/task/types.ts +2 -2
- package/src/tools/bash.ts +240 -57
- package/src/tools/cancel-job.ts +2 -1
- package/src/tools/find.ts +5 -2
- package/src/tools/grep.ts +77 -8
- package/src/tools/index.ts +48 -19
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/{await-tool.ts → poll-tool.ts} +38 -31
- package/src/tools/python.ts +293 -278
- package/src/tools/read.ts +218 -1
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/submit-result.ts +5 -2
- package/src/tools/todo-write.ts +8 -2
- package/src/tools/vim.ts +966 -0
- package/src/tools/write.ts +187 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/edit-mode.ts +2 -1
- package/src/utils/git.ts +24 -1
- package/src/utils/session-color.ts +55 -0
- package/src/utils/title-generator.ts +16 -7
- package/src/vim/buffer.ts +309 -0
- package/src/vim/commands.ts +382 -0
- package/src/vim/engine.ts +2426 -0
- package/src/vim/parser.ts +151 -0
- package/src/vim/render.ts +252 -0
- package/src/vim/types.ts +197 -0
package/src/tools/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { ToolChoice } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import type { SearchDb } from "@oh-my-pi/pi-natives";
|
|
4
3
|
import { $env, $flag, isBunTestRuntime, logger } from "@oh-my-pi/pi-utils";
|
|
5
4
|
import type { AsyncJobManager } from "../async";
|
|
6
5
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
@@ -8,7 +7,7 @@ import type { Settings } from "../config/settings";
|
|
|
8
7
|
import { EditTool } from "../edit";
|
|
9
8
|
import type { Skill } from "../extensibility/skills";
|
|
10
9
|
import type { InternalUrlRouter } from "../internal-urls";
|
|
11
|
-
import { getPreludeDocs, warmPythonEnvironment } from "../ipy/executor";
|
|
10
|
+
import { getPreludeDocs, resetPreludeDocsCache, warmPythonEnvironment } from "../ipy/executor";
|
|
12
11
|
import { checkPythonKernelAvailability } from "../ipy/kernel";
|
|
13
12
|
import { LspTool } from "../lsp";
|
|
14
13
|
import type { DiscoverableMCPSearchIndex, DiscoverableMCPTool } from "../mcp/discoverable-tool-metadata";
|
|
@@ -22,10 +21,8 @@ import { SearchTool } from "../web/search";
|
|
|
22
21
|
import { AskTool } from "./ask";
|
|
23
22
|
import { AstEditTool } from "./ast-edit";
|
|
24
23
|
import { AstGrepTool } from "./ast-grep";
|
|
25
|
-
import { AwaitTool } from "./await-tool";
|
|
26
24
|
import { BashTool } from "./bash";
|
|
27
25
|
import { BrowserTool } from "./browser";
|
|
28
|
-
|
|
29
26
|
import { CalculatorTool } from "./calculator";
|
|
30
27
|
import { CancelJobTool } from "./cancel-job";
|
|
31
28
|
import { type CheckpointState, CheckpointTool, RewindTool } from "./checkpoint";
|
|
@@ -47,6 +44,7 @@ import { GrepTool } from "./grep";
|
|
|
47
44
|
import { InspectImageTool } from "./inspect-image";
|
|
48
45
|
import { NotebookTool } from "./notebook";
|
|
49
46
|
import { wrapToolWithMetaNotice } from "./output-meta";
|
|
47
|
+
import { PollTool } from "./poll-tool";
|
|
50
48
|
import { PythonTool } from "./python";
|
|
51
49
|
import { ReadTool } from "./read";
|
|
52
50
|
import { RenderMermaidTool } from "./render-mermaid";
|
|
@@ -71,7 +69,6 @@ export * from "../web/search";
|
|
|
71
69
|
export * from "./ask";
|
|
72
70
|
export * from "./ast-edit";
|
|
73
71
|
export * from "./ast-grep";
|
|
74
|
-
export * from "./await-tool";
|
|
75
72
|
export * from "./bash";
|
|
76
73
|
export * from "./browser";
|
|
77
74
|
export * from "./calculator";
|
|
@@ -85,6 +82,7 @@ export * from "./gh";
|
|
|
85
82
|
export * from "./grep";
|
|
86
83
|
export * from "./inspect-image";
|
|
87
84
|
export * from "./notebook";
|
|
85
|
+
export * from "./poll-tool";
|
|
88
86
|
export * from "./python";
|
|
89
87
|
export * from "./read";
|
|
90
88
|
export * from "./render-mermaid";
|
|
@@ -95,6 +93,7 @@ export * from "./search-tool-bm25";
|
|
|
95
93
|
export * from "./ssh";
|
|
96
94
|
export * from "./submit-result";
|
|
97
95
|
export * from "./todo-write";
|
|
96
|
+
export * from "./vim";
|
|
98
97
|
export * from "./write";
|
|
99
98
|
|
|
100
99
|
/** Tool type (AgentTool from pi-ai) */
|
|
@@ -116,6 +115,8 @@ export interface ToolSession {
|
|
|
116
115
|
hasUI: boolean;
|
|
117
116
|
/** Skip Python kernel availability check and warmup */
|
|
118
117
|
skipPythonPreflight?: boolean;
|
|
118
|
+
/** Force Python prelude warmup even when test env would normally skip it */
|
|
119
|
+
forcePythonWarmup?: boolean;
|
|
119
120
|
/** Pre-loaded context files (AGENTS.md, etc) */
|
|
120
121
|
contextFiles?: ContextFileEntry[];
|
|
121
122
|
/** Pre-loaded skills */
|
|
@@ -124,7 +125,7 @@ export interface ToolSession {
|
|
|
124
125
|
promptTemplates?: PromptTemplate[];
|
|
125
126
|
/** Whether LSP integrations are enabled */
|
|
126
127
|
enableLsp?: boolean;
|
|
127
|
-
/** Whether
|
|
128
|
+
/** Whether an edit-capable tool is available in this session (controls hashline output) */
|
|
128
129
|
hasEditTool?: boolean;
|
|
129
130
|
/** Event bus for tool/extension communication */
|
|
130
131
|
eventBus?: EventBus;
|
|
@@ -136,6 +137,12 @@ export interface ToolSession {
|
|
|
136
137
|
taskDepth?: number;
|
|
137
138
|
/** Get session file */
|
|
138
139
|
getSessionFile: () => string | null;
|
|
140
|
+
/** Get Python kernel owner ID for session-scoped retained-kernel cleanup */
|
|
141
|
+
getPythonKernelOwnerId?: () => string | null;
|
|
142
|
+
/** Reject new Python work once session disposal has started. */
|
|
143
|
+
assertPythonExecutionAllowed?: () => void;
|
|
144
|
+
/** Track tool-owned Python work so session disposal can await/abort it like direct session Python runs. */
|
|
145
|
+
trackPythonExecution?<T>(execution: Promise<T>, abortController: AbortController): Promise<T>;
|
|
139
146
|
/** Get session ID */
|
|
140
147
|
getSessionId?: () => string | null;
|
|
141
148
|
/** Get artifacts directory for artifact:// URLs */
|
|
@@ -162,8 +169,6 @@ export interface ToolSession {
|
|
|
162
169
|
asyncJobManager?: AsyncJobManager;
|
|
163
170
|
/** Settings instance for passing to subagents */
|
|
164
171
|
settings: Settings;
|
|
165
|
-
/** Shared native search DB for grep/glob/fuzzyFind-backed workflows. */
|
|
166
|
-
searchDb?: SearchDb;
|
|
167
172
|
/** Plan mode state (if active) */
|
|
168
173
|
getPlanModeState?: () => PlanModeState | undefined;
|
|
169
174
|
/** Get compact conversation context for subagents (excludes tool results, system prompts) */
|
|
@@ -232,7 +237,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
232
237
|
rewind: RewindTool.createIf,
|
|
233
238
|
task: TaskTool.create,
|
|
234
239
|
cancel_job: CancelJobTool.createIf,
|
|
235
|
-
|
|
240
|
+
poll: PollTool.createIf,
|
|
236
241
|
todo_write: s => new TodoWriteTool(s),
|
|
237
242
|
web_search: s => new SearchTool(s),
|
|
238
243
|
search_tool_bm25: SearchToolBm25Tool.createIf,
|
|
@@ -297,7 +302,11 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
297
302
|
!skipPythonPreflight &&
|
|
298
303
|
pythonMode !== "bash-only" &&
|
|
299
304
|
(requestedTools === undefined || requestedTools.includes("python"));
|
|
300
|
-
const
|
|
305
|
+
const isTestEnv = isBunTestRuntime();
|
|
306
|
+
const forcePythonWarmup = session.forcePythonWarmup === true;
|
|
307
|
+
const skipPythonWarm = (isTestEnv && !forcePythonWarmup) || $flag("PI_PYTHON_SKIP_CHECK");
|
|
308
|
+
const cachedPreludeDocs = getPreludeDocs();
|
|
309
|
+
const shouldWarmPython = !skipPythonWarm && (forcePythonWarmup || cachedPreludeDocs.length === 0);
|
|
301
310
|
if (shouldCheckPython) {
|
|
302
311
|
const availability = await logger.time("createTools:pythonCheck", checkPythonKernelAvailability, session.cwd);
|
|
303
312
|
pythonAvailable = availability.ok;
|
|
@@ -305,18 +314,38 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
305
314
|
logger.warn("Python kernel unavailable, falling back to bash", {
|
|
306
315
|
reason: availability.reason,
|
|
307
316
|
});
|
|
308
|
-
} else if (
|
|
317
|
+
} else if (shouldWarmPython) {
|
|
309
318
|
const sessionFile = session.getSessionFile?.() ?? undefined;
|
|
319
|
+
const kernelOwnerId = session.getPythonKernelOwnerId?.() ?? undefined;
|
|
310
320
|
const warmSessionId = sessionFile ? `session:${sessionFile}:cwd:${session.cwd}` : `cwd:${session.cwd}`;
|
|
321
|
+
const warmupAbortController = new AbortController();
|
|
311
322
|
try {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
323
|
+
session.assertPythonExecutionAllowed?.();
|
|
324
|
+
if (forcePythonWarmup && cachedPreludeDocs.length > 0) {
|
|
325
|
+
resetPreludeDocsCache();
|
|
326
|
+
}
|
|
327
|
+
const warmupExecution = session.trackPythonExecution
|
|
328
|
+
? logger.time(
|
|
329
|
+
"createTools:warmPython",
|
|
330
|
+
warmPythonEnvironment,
|
|
331
|
+
session.cwd,
|
|
332
|
+
warmSessionId,
|
|
333
|
+
session.settings.get("python.sharedGateway"),
|
|
334
|
+
sessionFile,
|
|
335
|
+
kernelOwnerId,
|
|
336
|
+
warmupAbortController.signal,
|
|
337
|
+
)
|
|
338
|
+
: logger.time(
|
|
339
|
+
"createTools:warmPython",
|
|
340
|
+
warmPythonEnvironment,
|
|
341
|
+
session.cwd,
|
|
342
|
+
warmSessionId,
|
|
343
|
+
session.settings.get("python.sharedGateway"),
|
|
344
|
+
sessionFile,
|
|
345
|
+
kernelOwnerId,
|
|
346
|
+
);
|
|
347
|
+
await (session.trackPythonExecution?.(warmupExecution, warmupAbortController) ?? warmupExecution);
|
|
348
|
+
session.assertPythonExecutionAllowed?.();
|
|
320
349
|
} catch (err) {
|
|
321
350
|
logger.warn("Failed to warm Python environment", {
|
|
322
351
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -79,7 +79,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
79
79
|
const resolvePattern = (pattern: string | undefined): Model<Api> | undefined => {
|
|
80
80
|
if (!pattern) return undefined;
|
|
81
81
|
const expanded = expandRoleAlias(pattern, this.session.settings);
|
|
82
|
-
return resolveModelFromString(expanded, availableModels, matchPreferences);
|
|
82
|
+
return resolveModelFromString(expanded, availableModels, matchPreferences, modelRegistry);
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
const activeModelPattern = this.session.getActiveModelString?.() ?? this.session.getModelString?.();
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import { type Static, Type } from "@sinclair/typebox";
|
|
4
|
-
import
|
|
4
|
+
import { isBackgroundJobSupportEnabled } from "../async";
|
|
5
|
+
import pollDescription from "../prompts/tools/poll.md" with { type: "text" };
|
|
5
6
|
import type { ToolSession } from "./index";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const pollSchema = Type.Object({
|
|
8
9
|
jobs: Type.Optional(
|
|
9
10
|
Type.Array(Type.String(), {
|
|
10
11
|
description: "Specific job IDs to wait for. If omitted, waits for any running job.",
|
|
@@ -12,9 +13,9 @@ const awaitSchema = Type.Object({
|
|
|
12
13
|
),
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
type
|
|
16
|
+
type PollParams = Static<typeof pollSchema>;
|
|
16
17
|
|
|
17
|
-
interface
|
|
18
|
+
interface PollResult {
|
|
18
19
|
id: string;
|
|
19
20
|
type: "bash" | "task";
|
|
20
21
|
status: "running" | "completed" | "failed" | "cancelled";
|
|
@@ -24,33 +25,33 @@ interface AwaitResult {
|
|
|
24
25
|
errorText?: string;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export interface
|
|
28
|
-
jobs:
|
|
28
|
+
export interface PollToolDetails {
|
|
29
|
+
jobs: PollResult[];
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
export class
|
|
32
|
-
readonly name = "
|
|
33
|
-
readonly label = "
|
|
32
|
+
export class PollTool implements AgentTool<typeof pollSchema, PollToolDetails> {
|
|
33
|
+
readonly name = "poll";
|
|
34
|
+
readonly label = "Poll";
|
|
34
35
|
readonly description: string;
|
|
35
|
-
readonly parameters =
|
|
36
|
+
readonly parameters = pollSchema;
|
|
36
37
|
readonly strict = true;
|
|
37
38
|
|
|
38
39
|
constructor(private readonly session: ToolSession) {
|
|
39
|
-
this.description = prompt.render(
|
|
40
|
+
this.description = prompt.render(pollDescription);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
static createIf(session: ToolSession):
|
|
43
|
-
if (!session.settings
|
|
44
|
-
return new
|
|
43
|
+
static createIf(session: ToolSession): PollTool | null {
|
|
44
|
+
if (!isBackgroundJobSupportEnabled(session.settings)) return null;
|
|
45
|
+
return new PollTool(session);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
async execute(
|
|
48
49
|
_toolCallId: string,
|
|
49
|
-
params:
|
|
50
|
+
params: PollParams,
|
|
50
51
|
signal?: AbortSignal,
|
|
51
|
-
_onUpdate?: AgentToolUpdateCallback<
|
|
52
|
+
_onUpdate?: AgentToolUpdateCallback<PollToolDetails>,
|
|
52
53
|
_context?: AgentToolContext,
|
|
53
|
-
): Promise<AgentToolResult<
|
|
54
|
+
): Promise<AgentToolResult<PollToolDetails>> {
|
|
54
55
|
const manager = this.session.asyncJobManager;
|
|
55
56
|
if (!manager) {
|
|
56
57
|
return {
|
|
@@ -84,19 +85,25 @@ export class AwaitTool implements AgentTool<typeof awaitSchema, AwaitToolDetails
|
|
|
84
85
|
|
|
85
86
|
// Block until at least one running job finishes or the call is aborted
|
|
86
87
|
const racePromises: Promise<unknown>[] = runningJobs.map(j => j.promise);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
signal
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
const watchedJobIds = runningJobs.map(job => job.id);
|
|
89
|
+
manager.watchJobs(watchedJobIds);
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
if (signal) {
|
|
93
|
+
const { promise: abortPromise, resolve: abortResolve } = Promise.withResolvers<void>();
|
|
94
|
+
const onAbort = () => abortResolve();
|
|
95
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
96
|
+
racePromises.push(abortPromise);
|
|
97
|
+
try {
|
|
98
|
+
await Promise.race(racePromises);
|
|
99
|
+
} finally {
|
|
100
|
+
signal.removeEventListener("abort", onAbort);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
94
103
|
await Promise.race(racePromises);
|
|
95
|
-
} finally {
|
|
96
|
-
signal.removeEventListener("abort", onAbort);
|
|
97
104
|
}
|
|
98
|
-
}
|
|
99
|
-
|
|
105
|
+
} finally {
|
|
106
|
+
manager.unwatchJobs(watchedJobIds);
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
if (signal?.aborted) {
|
|
@@ -117,12 +124,12 @@ export class AwaitTool implements AgentTool<typeof awaitSchema, AwaitToolDetails
|
|
|
117
124
|
resultText?: string;
|
|
118
125
|
errorText?: string;
|
|
119
126
|
}[],
|
|
120
|
-
): AgentToolResult<
|
|
127
|
+
): AgentToolResult<PollToolDetails> {
|
|
121
128
|
const now = Date.now();
|
|
122
|
-
const jobResults:
|
|
129
|
+
const jobResults: PollResult[] = jobs.map(j => ({
|
|
123
130
|
id: j.id,
|
|
124
131
|
type: j.type,
|
|
125
|
-
status: j.status as
|
|
132
|
+
status: j.status as PollResult["status"],
|
|
126
133
|
label: j.label,
|
|
127
134
|
durationMs: Math.max(0, now - j.startTime),
|
|
128
135
|
...(j.resultText ? { resultText: j.resultText } : {}),
|