@oh-my-pi/pi-coding-agent 8.3.0 → 8.4.0
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 +17 -0
- package/package.json +6 -6
- package/src/config/model-registry.ts +8 -0
- package/src/discovery/helpers.ts +12 -2
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/ipy/executor.ts +4 -0
- package/src/modes/controllers/event-controller.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +2 -2
- package/src/modes/interactive-mode.ts +30 -0
- package/src/modes/rpc/rpc-mode.ts +4 -0
- package/src/modes/theme/theme.ts +10 -107
- package/src/modes/types.ts +2 -0
- package/src/patch/applicator.ts +202 -38
- package/src/patch/fuzzy.ts +135 -25
- package/src/patch/index.ts +25 -2
- package/src/patch/parser.ts +5 -0
- package/src/patch/types.ts +25 -0
- package/src/sdk.ts +6 -0
- package/src/session/agent-session.ts +1 -0
- package/src/session/session-manager.ts +3 -0
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +49 -17
- package/src/task/index.ts +16 -3
- package/src/task/types.ts +3 -3
- package/src/task/worker-protocol.ts +8 -0
- package/src/task/worker.ts +11 -0
- package/src/tools/index.ts +19 -1
package/src/task/executor.ts
CHANGED
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
8
|
-
import type {
|
|
8
|
+
import type { PromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
9
|
+
import type { Skill } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
|
|
10
|
+
import { getPreludeDocs } from "@oh-my-pi/pi-coding-agent/ipy/executor";
|
|
11
|
+
import { checkPythonKernelAvailability } from "@oh-my-pi/pi-coding-agent/ipy/kernel";
|
|
12
|
+
import type { ContextFileEntry, ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
|
9
13
|
import type { ModelRegistry } from "../config/model-registry";
|
|
10
14
|
import { formatModelString, parseModelPattern } from "../config/model-resolver";
|
|
11
|
-
import { checkPythonKernelAvailability } from "../ipy/kernel";
|
|
12
15
|
import { LspTool } from "../lsp";
|
|
13
16
|
import type { LspParams } from "../lsp/types";
|
|
14
17
|
import { callTool } from "../mcp/client";
|
|
@@ -37,6 +40,19 @@ import type {
|
|
|
37
40
|
SubagentWorkerResponse,
|
|
38
41
|
} from "./worker-protocol";
|
|
39
42
|
|
|
43
|
+
const DEFAULT_MODEL_ALIASES = new Set(["default", "pi/default", "omp/default"]);
|
|
44
|
+
|
|
45
|
+
function normalizeModelPatterns(value: string | string[] | undefined): string[] {
|
|
46
|
+
if (!value) return [];
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
return value.map(entry => entry.trim()).filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
return value
|
|
51
|
+
.split(",")
|
|
52
|
+
.map(entry => entry.trim())
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
/** Options for worker execution */
|
|
41
57
|
export interface ExecutorOptions {
|
|
42
58
|
cwd: string;
|
|
@@ -47,7 +63,7 @@ export interface ExecutorOptions {
|
|
|
47
63
|
index: number;
|
|
48
64
|
id: string;
|
|
49
65
|
context?: string;
|
|
50
|
-
modelOverride?: string;
|
|
66
|
+
modelOverride?: string | string[];
|
|
51
67
|
thinkingLevel?: ThinkingLevel;
|
|
52
68
|
outputSchema?: unknown;
|
|
53
69
|
enableLsp?: boolean;
|
|
@@ -57,6 +73,9 @@ export interface ExecutorOptions {
|
|
|
57
73
|
persistArtifacts?: boolean;
|
|
58
74
|
artifactsDir?: string;
|
|
59
75
|
eventBus?: EventBus;
|
|
76
|
+
contextFiles?: ContextFileEntry[];
|
|
77
|
+
skills?: Skill[];
|
|
78
|
+
promptTemplates?: PromptTemplate[];
|
|
60
79
|
mcpManager?: MCPManager;
|
|
61
80
|
authStorage?: AuthStorage;
|
|
62
81
|
modelRegistry?: ModelRegistry;
|
|
@@ -277,23 +296,30 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
277
296
|
const serializedSettings = options.settingsManager?.serialize();
|
|
278
297
|
const availableModels = options.modelRegistry?.getAvailable() ?? [];
|
|
279
298
|
|
|
280
|
-
// Resolve model pattern to provider/modelId string
|
|
281
|
-
const
|
|
299
|
+
// Resolve model pattern list to provider/modelId string
|
|
300
|
+
const modelPatterns = normalizeModelPatterns(modelOverride ?? agent.model);
|
|
282
301
|
let resolvedModel: string | undefined;
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (
|
|
292
|
-
|
|
302
|
+
if (modelPatterns.length > 0) {
|
|
303
|
+
const roles = serializedSettings?.modelRoles as Record<string, string> | undefined;
|
|
304
|
+
for (const pattern of modelPatterns) {
|
|
305
|
+
const normalized = pattern.trim().toLowerCase();
|
|
306
|
+
if (!normalized || DEFAULT_MODEL_ALIASES.has(normalized)) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
let effectivePattern = pattern;
|
|
310
|
+
if (normalized.startsWith("omp/") || normalized.startsWith("pi/")) {
|
|
311
|
+
const role = normalized.startsWith("omp/") ? pattern.slice(4) : pattern.slice(3);
|
|
312
|
+
const configured = roles?.[role] ?? roles?.[role.toLowerCase()];
|
|
313
|
+
if (configured) {
|
|
314
|
+
effectivePattern = configured;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const { model } = parseModelPattern(effectivePattern, availableModels);
|
|
318
|
+
if (model) {
|
|
319
|
+
resolvedModel = formatModelString(model);
|
|
320
|
+
break;
|
|
293
321
|
}
|
|
294
322
|
}
|
|
295
|
-
const { model } = parseModelPattern(effectivePattern, availableModels);
|
|
296
|
-
resolvedModel = model ? formatModelString(model) : undefined;
|
|
297
323
|
}
|
|
298
324
|
const sessionFile = subtaskSessionFile ?? null;
|
|
299
325
|
const spawnsEnv = agent.spawns === undefined ? "" : agent.spawns === "*" ? "*" : agent.spawns.join(",");
|
|
@@ -307,6 +333,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
307
333
|
|
|
308
334
|
const lspEnabled = enableLsp ?? true;
|
|
309
335
|
const lspToolRequested = lspEnabled && (toolNames === undefined || toolNames.includes("lsp"));
|
|
336
|
+
const pythonPreludeDocs = getPreludeDocs();
|
|
337
|
+
const pythonPreludeDocsPayload = pythonPreludeDocs.length > 0 ? pythonPreludeDocs : undefined;
|
|
310
338
|
|
|
311
339
|
let worker: Worker;
|
|
312
340
|
try {
|
|
@@ -692,6 +720,10 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
692
720
|
serializedAuth: options.authStorage?.serialize(),
|
|
693
721
|
serializedModels: options.modelRegistry?.serialize(),
|
|
694
722
|
serializedSettings,
|
|
723
|
+
pythonPreludeDocs: pythonPreludeDocsPayload,
|
|
724
|
+
contextFiles: options.contextFiles,
|
|
725
|
+
skills: options.skills,
|
|
726
|
+
promptTemplates: options.promptTemplates,
|
|
695
727
|
mcpTools: options.mcpManager ? extractMCPToolMetadata(options.mcpManager) : undefined,
|
|
696
728
|
pythonToolProxy: pythonProxyEnabled,
|
|
697
729
|
lspToolProxy: Boolean(lspTool),
|
package/src/task/index.ts
CHANGED
|
@@ -163,10 +163,14 @@ export class TaskTool implements AgentTool<typeof taskSchema, TaskToolDetails, T
|
|
|
163
163
|
const { agent: agentName, context, output: outputSchema, isolated } = params;
|
|
164
164
|
const isIsolated = isolated === true;
|
|
165
165
|
|
|
166
|
-
const isDefaultModelAlias = (value: string | undefined): boolean => {
|
|
166
|
+
const isDefaultModelAlias = (value: string | string[] | undefined): boolean => {
|
|
167
167
|
if (!value) return true;
|
|
168
|
-
const
|
|
169
|
-
|
|
168
|
+
const values = Array.isArray(value) ? value : [value];
|
|
169
|
+
if (values.length === 0) return true;
|
|
170
|
+
return values.every(entry => {
|
|
171
|
+
const normalized = entry.trim().toLowerCase();
|
|
172
|
+
return normalized === "default" || normalized === "pi/default" || normalized === "omp/default";
|
|
173
|
+
});
|
|
170
174
|
};
|
|
171
175
|
|
|
172
176
|
// Validate agent exists
|
|
@@ -374,6 +378,9 @@ export class TaskTool implements AgentTool<typeof taskSchema, TaskToolDetails, T
|
|
|
374
378
|
|
|
375
379
|
// Build full prompts with context prepended
|
|
376
380
|
const tasksWithContext = tasksWithUniqueIds.map(t => renderTemplate(context, t));
|
|
381
|
+
const contextFiles = this.session.contextFiles;
|
|
382
|
+
const skills = this.session.skills;
|
|
383
|
+
const promptTemplates = this.session.promptTemplates;
|
|
377
384
|
|
|
378
385
|
// Initialize progress for all tasks
|
|
379
386
|
for (let i = 0; i < tasksWithContext.length; i++) {
|
|
@@ -427,6 +434,9 @@ export class TaskTool implements AgentTool<typeof taskSchema, TaskToolDetails, T
|
|
|
427
434
|
modelRegistry: this.session.modelRegistry,
|
|
428
435
|
settingsManager: this.session.settingsManager,
|
|
429
436
|
mcpManager: this.session.mcpManager,
|
|
437
|
+
contextFiles,
|
|
438
|
+
skills,
|
|
439
|
+
promptTemplates,
|
|
430
440
|
});
|
|
431
441
|
}
|
|
432
442
|
|
|
@@ -467,6 +477,9 @@ export class TaskTool implements AgentTool<typeof taskSchema, TaskToolDetails, T
|
|
|
467
477
|
modelRegistry: this.session.modelRegistry,
|
|
468
478
|
settingsManager: this.session.settingsManager,
|
|
469
479
|
mcpManager: this.session.mcpManager,
|
|
480
|
+
contextFiles,
|
|
481
|
+
skills,
|
|
482
|
+
promptTemplates,
|
|
470
483
|
});
|
|
471
484
|
const patch = await captureDeltaPatch(worktreeDir, baseline);
|
|
472
485
|
const patchPath = path.join(effectiveArtifactsDir, `${task.id}.patch`);
|
package/src/task/types.ts
CHANGED
|
@@ -97,7 +97,7 @@ export interface AgentDefinition {
|
|
|
97
97
|
systemPrompt: string;
|
|
98
98
|
tools?: string[];
|
|
99
99
|
spawns?: string[] | "*";
|
|
100
|
-
model?: string;
|
|
100
|
+
model?: string[];
|
|
101
101
|
thinkingLevel?: ThinkingLevel;
|
|
102
102
|
output?: unknown;
|
|
103
103
|
source: AgentSource;
|
|
@@ -122,7 +122,7 @@ export interface AgentProgress {
|
|
|
122
122
|
toolCount: number;
|
|
123
123
|
tokens: number;
|
|
124
124
|
durationMs: number;
|
|
125
|
-
modelOverride?: string;
|
|
125
|
+
modelOverride?: string | string[];
|
|
126
126
|
/** Data extracted by registered subprocess tool handlers (keyed by tool name) */
|
|
127
127
|
extractedToolData?: Record<string, unknown[]>;
|
|
128
128
|
}
|
|
@@ -142,7 +142,7 @@ export interface SingleResult {
|
|
|
142
142
|
truncated: boolean;
|
|
143
143
|
durationMs: number;
|
|
144
144
|
tokens: number;
|
|
145
|
-
modelOverride?: string;
|
|
145
|
+
modelOverride?: string | string[];
|
|
146
146
|
error?: string;
|
|
147
147
|
aborted?: boolean;
|
|
148
148
|
/** Aggregated usage from the subprocess, accumulated incrementally from message_end events. */
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { PromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
3
|
+
import type { Skill } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
|
|
4
|
+
import type { PreludeHelper } from "@oh-my-pi/pi-coding-agent/ipy/kernel";
|
|
5
|
+
import type { ContextFileEntry } from "@oh-my-pi/pi-coding-agent/tools";
|
|
2
6
|
import type { SerializedModelRegistry } from "../config/model-registry";
|
|
3
7
|
import type { Settings } from "../config/settings-manager";
|
|
4
8
|
import type { SerializedAuthStorage } from "../session/auth-storage";
|
|
@@ -100,6 +104,10 @@ export interface SubagentWorkerStartPayload {
|
|
|
100
104
|
serializedAuth?: SerializedAuthStorage;
|
|
101
105
|
serializedModels?: SerializedModelRegistry;
|
|
102
106
|
serializedSettings?: Settings;
|
|
107
|
+
pythonPreludeDocs?: PreludeHelper[];
|
|
108
|
+
contextFiles?: ContextFileEntry[];
|
|
109
|
+
skills?: Skill[];
|
|
110
|
+
promptTemplates?: PromptTemplate[];
|
|
103
111
|
mcpTools?: MCPToolMetadata[];
|
|
104
112
|
pythonToolProxy?: boolean;
|
|
105
113
|
lspToolProxy?: boolean;
|
package/src/task/worker.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
16
16
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
17
|
+
import { setPreludeDocsCache } from "@oh-my-pi/pi-coding-agent/ipy/executor";
|
|
17
18
|
import { logger, postmortem, untilAborted } from "@oh-my-pi/pi-utils";
|
|
18
19
|
import type { TSchema } from "@sinclair/typebox";
|
|
19
20
|
import { ModelRegistry } from "../config/model-registry";
|
|
@@ -527,6 +528,9 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload):
|
|
|
527
528
|
let error: string | undefined;
|
|
528
529
|
let aborted = false;
|
|
529
530
|
const sessionAbortController = new AbortController();
|
|
531
|
+
if (payload.pythonPreludeDocs && payload.pythonPreludeDocs.length > 0) {
|
|
532
|
+
setPreludeDocsCache(payload.pythonPreludeDocs);
|
|
533
|
+
}
|
|
530
534
|
|
|
531
535
|
// Helper to check abort status - throws if aborted to exit early
|
|
532
536
|
const checkAbort = (): void => {
|
|
@@ -589,6 +593,9 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload):
|
|
|
589
593
|
? `You will work under this working tree: ${payload.worktree}. CRITICAL: Do not touch the original repository; only make changes inside this worktree.`
|
|
590
594
|
: "";
|
|
591
595
|
|
|
596
|
+
const skipPythonPreflight =
|
|
597
|
+
payload.pythonToolProxy === true ||
|
|
598
|
+
(Array.isArray(payload.toolNames) && !payload.toolNames.includes("python"));
|
|
592
599
|
const { session } = await createAgentSession({
|
|
593
600
|
cwd: payload.worktree ?? payload.cwd,
|
|
594
601
|
authStorage,
|
|
@@ -599,6 +606,9 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload):
|
|
|
599
606
|
toolNames: payload.toolNames,
|
|
600
607
|
outputSchema: payload.outputSchema,
|
|
601
608
|
requireCompleteTool: true,
|
|
609
|
+
contextFiles: payload.contextFiles,
|
|
610
|
+
skills: payload.skills,
|
|
611
|
+
promptTemplates: payload.promptTemplates,
|
|
602
612
|
// Append system prompt (equivalent to CLI's --append-system-prompt)
|
|
603
613
|
systemPrompt: defaultPrompt =>
|
|
604
614
|
`${defaultPrompt}\n\n${payload.systemPrompt}\n\n${worktreeNotice}\n\n${completionInstruction}`,
|
|
@@ -607,6 +617,7 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload):
|
|
|
607
617
|
// Pass spawn restrictions to nested tasks
|
|
608
618
|
spawns: payload.spawnsEnv,
|
|
609
619
|
enableLsp: enableLsp && !lspProxyEnabled,
|
|
620
|
+
skipPythonPreflight,
|
|
610
621
|
// Disable local MCP discovery if using proxy tools
|
|
611
622
|
enableMCP: !payload.mcpTools,
|
|
612
623
|
// Add proxy tools
|
package/src/tools/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { PromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
3
|
+
import type { Skill } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
|
|
2
4
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
3
5
|
import type { BashInterceptorRule } from "../config/settings-manager";
|
|
4
6
|
import type { InternalUrlRouter } from "../internal-urls";
|
|
@@ -94,12 +96,26 @@ export { WriteTool, type WriteToolDetails } from "./write";
|
|
|
94
96
|
/** Tool type (AgentTool from pi-ai) */
|
|
95
97
|
export type Tool = AgentTool<any, any, any>;
|
|
96
98
|
|
|
99
|
+
export type ContextFileEntry = {
|
|
100
|
+
path: string;
|
|
101
|
+
content: string;
|
|
102
|
+
depth?: number;
|
|
103
|
+
};
|
|
104
|
+
|
|
97
105
|
/** Session context for tool factories */
|
|
98
106
|
export interface ToolSession {
|
|
99
107
|
/** Current working directory */
|
|
100
108
|
cwd: string;
|
|
101
109
|
/** Whether UI is available */
|
|
102
110
|
hasUI: boolean;
|
|
111
|
+
/** Skip Python kernel availability check and warmup */
|
|
112
|
+
skipPythonPreflight?: boolean;
|
|
113
|
+
/** Pre-loaded context files (AGENTS.md, etc) */
|
|
114
|
+
contextFiles?: ContextFileEntry[];
|
|
115
|
+
/** Pre-loaded skills */
|
|
116
|
+
skills?: Skill[];
|
|
117
|
+
/** Pre-loaded prompt templates */
|
|
118
|
+
promptTemplates?: PromptTemplate[];
|
|
103
119
|
/** Whether LSP integrations are enabled */
|
|
104
120
|
enableLsp?: boolean;
|
|
105
121
|
/** Event bus for tool/extension communication */
|
|
@@ -219,10 +235,12 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
219
235
|
const enableLsp = session.enableLsp ?? true;
|
|
220
236
|
const requestedTools = toolNames && toolNames.length > 0 ? [...new Set(toolNames)] : undefined;
|
|
221
237
|
const pythonMode = getPythonModeFromEnv() ?? session.settings?.getPythonToolMode?.() ?? "ipy-only";
|
|
238
|
+
const skipPythonPreflight = session.skipPythonPreflight === true;
|
|
222
239
|
let pythonAvailable = true;
|
|
223
240
|
const shouldCheckPython =
|
|
241
|
+
!skipPythonPreflight &&
|
|
224
242
|
pythonMode !== "bash-only" &&
|
|
225
|
-
(requestedTools === undefined || requestedTools.includes("python")
|
|
243
|
+
(requestedTools === undefined || requestedTools.includes("python"));
|
|
226
244
|
const isTestEnv = process.env.BUN_ENV === "test" || process.env.NODE_ENV === "test";
|
|
227
245
|
const skipPythonWarm = isTestEnv || process.env.OMP_PYTHON_SKIP_CHECK === "1";
|
|
228
246
|
if (shouldCheckPython) {
|