@oh-my-pi/pi-coding-agent 16.2.11 → 16.2.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 +31 -0
- package/dist/cli.js +3005 -2848
- package/dist/types/cli/bench-cli.d.ts +0 -5
- package/dist/types/cli/dry-balance-cli.d.ts +0 -5
- package/dist/types/cli/models-cli.d.ts +1 -1
- package/dist/types/config/inline-tool-descriptors-mode.d.ts +1 -2
- package/dist/types/config/model-registry.d.ts +1 -23
- package/dist/types/config/model-resolver.d.ts +10 -15
- package/dist/types/config/models-config-schema.d.ts +0 -6
- package/dist/types/config/models-config.d.ts +0 -6
- package/dist/types/config/settings-schema.d.ts +31 -1
- package/dist/types/eval/py/__tests__/runner-shell-output.test.d.ts +1 -0
- package/dist/types/extensibility/extensions/runner.d.ts +15 -0
- package/dist/types/lsp/client.d.ts +9 -3
- package/dist/types/modes/controllers/tool-args-reveal.d.ts +0 -5
- package/dist/types/task/spawn-policy.d.ts +17 -0
- package/dist/types/task/spawn-policy.test.d.ts +1 -0
- package/dist/types/task/types.d.ts +8 -2
- package/dist/types/tools/__tests__/eval-description.test.d.ts +1 -0
- package/dist/types/tools/browser/cmux/cmux-tab.d.ts +2 -2
- package/dist/types/tools/browser/registry.d.ts +2 -0
- package/dist/types/tools/browser/run-cancellation.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +30 -1
- package/dist/types/tools/eval.d.ts +3 -6
- package/dist/types/tools/write.d.ts +1 -1
- package/dist/types/utils/image-vision-fallback.d.ts +1 -1
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +1 -1
- package/src/auto-thinking/classifier.ts +1 -1
- package/src/cli/bench-cli.ts +3 -12
- package/src/cli/dry-balance-cli.ts +1 -6
- package/src/cli/models-cli.ts +3 -81
- package/src/commands/models.ts +1 -2
- package/src/commit/model-selection.ts +4 -4
- package/src/config/inline-tool-descriptors-mode.ts +1 -2
- package/src/config/model-discovery.ts +32 -6
- package/src/config/model-registry.ts +11 -225
- package/src/config/model-resolver.ts +23 -135
- package/src/config/models-config-schema.ts +0 -22
- package/src/config/prompt-templates.ts +20 -0
- package/src/config/settings-schema.ts +35 -1
- package/src/config/settings.ts +60 -24
- package/src/eval/__tests__/agent-bridge.test.ts +17 -3
- package/src/eval/agent-bridge.ts +7 -9
- package/src/eval/completion-bridge.ts +1 -1
- package/src/eval/py/__tests__/runner-shell-output.test.ts +157 -0
- package/src/eval/py/runner.py +169 -18
- package/src/extensibility/extensions/model-api.ts +1 -3
- package/src/extensibility/extensions/runner.ts +62 -5
- package/src/lsp/client.ts +162 -45
- package/src/lsp/index.ts +31 -21
- package/src/main.ts +0 -1
- package/src/mcp/transports/http.ts +19 -17
- package/src/mcp/transports/stdio.test.ts +51 -1
- package/src/mcp/transports/stdio.ts +19 -9
- package/src/memories/index.ts +0 -1
- package/src/mnemopi/backend.ts +1 -1
- package/src/modes/components/model-selector.ts +32 -186
- package/src/modes/controllers/event-controller.ts +8 -39
- package/src/modes/controllers/selector-controller.ts +0 -1
- package/src/modes/controllers/tool-args-reveal.ts +0 -12
- package/src/prompts/system/subagent-system-prompt.md +2 -2
- package/src/prompts/system/tool-call-loop-redirect.md +8 -0
- package/src/prompts/tools/eval.md +2 -2
- package/src/prompts/tools/task.md +1 -1
- package/src/sdk.ts +2 -9
- package/src/session/agent-session.ts +89 -8
- package/src/session/session-context.ts +2 -1
- package/src/session/session-manager.ts +2 -1
- package/src/session/unexpected-stop-classifier.ts +1 -1
- package/src/task/index.ts +23 -27
- package/src/task/spawn-policy.test.ts +63 -0
- package/src/task/spawn-policy.ts +58 -0
- package/src/task/types.ts +77 -6
- package/src/tools/__tests__/eval-description.test.ts +19 -0
- package/src/tools/browser/cmux/cmux-tab.ts +66 -24
- package/src/tools/browser/registry.ts +25 -0
- package/src/tools/browser/run-cancellation.ts +47 -0
- package/src/tools/browser/tab-supervisor.ts +120 -7
- package/src/tools/browser/tab-worker.ts +22 -10
- package/src/tools/browser.ts +1 -0
- package/src/tools/eval.ts +15 -10
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/write.ts +49 -9
- package/src/utils/commit-message-generator.ts +0 -1
- package/src/utils/image-vision-fallback.ts +2 -3
- package/src/utils/title-generator.ts +1 -1
|
@@ -152,7 +152,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
152
152
|
const resolvePattern = (pattern: string | undefined): Model<Api> | undefined => {
|
|
153
153
|
if (!pattern) return undefined;
|
|
154
154
|
const expanded = expandRoleAlias(pattern, this.session.settings);
|
|
155
|
-
return resolveModelFromString(expanded, availableModels, matchPreferences
|
|
155
|
+
return resolveModelFromString(expanded, availableModels, matchPreferences);
|
|
156
156
|
};
|
|
157
157
|
|
|
158
158
|
const activeModelPattern = this.session.getActiveModelString?.() ?? this.session.getModelString?.();
|
package/src/tools/write.ts
CHANGED
|
@@ -52,6 +52,7 @@ import { enforcePlanModeWrite, resolvePlanPath, unwrapHashlineHeaderPath } from
|
|
|
52
52
|
import {
|
|
53
53
|
cachedRenderedString,
|
|
54
54
|
createRenderedStringCache,
|
|
55
|
+
Ellipsis,
|
|
55
56
|
formatDiagnostics,
|
|
56
57
|
formatErrorDetail,
|
|
57
58
|
formatExpandHint,
|
|
@@ -61,6 +62,8 @@ import {
|
|
|
61
62
|
type RenderedStringCache,
|
|
62
63
|
replaceTabs,
|
|
63
64
|
shortenPath,
|
|
65
|
+
TRUNCATE_LENGTHS,
|
|
66
|
+
truncateToWidth,
|
|
64
67
|
} from "./render-utils";
|
|
65
68
|
import {
|
|
66
69
|
deleteRowByKey,
|
|
@@ -165,6 +168,18 @@ function appendNoteToResult(result: AgentToolResult<WriteToolDetails>, note: str
|
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
170
|
|
|
171
|
+
function emitWriteProgress(
|
|
172
|
+
onUpdate: AgentToolUpdateCallback<WriteToolDetails> | undefined,
|
|
173
|
+
content: string,
|
|
174
|
+
displayPath: string,
|
|
175
|
+
resolvedPath?: string,
|
|
176
|
+
): void {
|
|
177
|
+
onUpdate?.({
|
|
178
|
+
content: [{ type: "text", text: `Writing ${content.length} bytes to ${shortenPath(displayPath)}...` }],
|
|
179
|
+
details: resolvedPath ? { resolvedPath } : {},
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
168
183
|
/**
|
|
169
184
|
* If `content` begins with a `#!` shebang, ensure the file is executable.
|
|
170
185
|
*
|
|
@@ -781,7 +796,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
781
796
|
_toolCallId: string,
|
|
782
797
|
{ path: rawPath, content }: WriteParams,
|
|
783
798
|
signal?: AbortSignal,
|
|
784
|
-
|
|
799
|
+
onUpdate?: AgentToolUpdateCallback<WriteToolDetails>,
|
|
785
800
|
context?: AgentToolContext,
|
|
786
801
|
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
787
802
|
// Strip a hashline `[path#TAG]` wrapper up front so every downstream
|
|
@@ -807,6 +822,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
807
822
|
// Handler-owned writes (vault:// notes, host URIs) mutate user
|
|
808
823
|
// data outside the local sandbox — plan mode must reject them.
|
|
809
824
|
enforcePlanModeWrite(this.session, path, { op: "update" });
|
|
825
|
+
emitWriteProgress(onUpdate, cleanContent, path);
|
|
810
826
|
await handler.write(parsed, cleanContent, { cwd: this.session.cwd, signal });
|
|
811
827
|
let resultText = `Successfully wrote ${cleanContent.length} bytes to ${path}`;
|
|
812
828
|
if (stripped) {
|
|
@@ -826,6 +842,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
826
842
|
`Conflict URI scope '/${conflictUri.scope}' is read-only — read \`conflict://${conflictUri.id}/${conflictUri.scope}\` to inspect that side. To write, drop the scope (\`conflict://${conflictUri.id}\`) and put the chosen content (or shorthand like \`@${conflictUri.scope}\`) in \`content\`.`,
|
|
827
843
|
);
|
|
828
844
|
}
|
|
845
|
+
emitWriteProgress(onUpdate, cleanContent, path);
|
|
829
846
|
const result =
|
|
830
847
|
conflictUri.id === "*"
|
|
831
848
|
? await this.#resolveAllConflicts(cleanContent, stripped, signal)
|
|
@@ -844,6 +861,14 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
844
861
|
op: resolvedArchivePath.exists ? "update" : "create",
|
|
845
862
|
});
|
|
846
863
|
|
|
864
|
+
emitWriteProgress(
|
|
865
|
+
onUpdate,
|
|
866
|
+
cleanContent,
|
|
867
|
+
`${formatPathRelativeToCwd(resolvedArchivePath.absolutePath, this.session.cwd)}:${
|
|
868
|
+
resolvedArchivePath.archiveSubPath
|
|
869
|
+
}`,
|
|
870
|
+
resolvedArchivePath.absolutePath,
|
|
871
|
+
);
|
|
847
872
|
const archiveResult = await this.#writeArchiveEntry(cleanContent, resolvedArchivePath);
|
|
848
873
|
if (stripped) {
|
|
849
874
|
const firstText = archiveResult.content.find(
|
|
@@ -861,6 +886,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
861
886
|
if (resolvedSqlitePath) {
|
|
862
887
|
enforcePlanModeWrite(this.session, resolvedSqlitePath.sqlitePath, { op: "update" });
|
|
863
888
|
|
|
889
|
+
emitWriteProgress(onUpdate, cleanContent, path, resolvedSqlitePath.absolutePath);
|
|
864
890
|
const sqliteResult = await this.#writeSqliteRow(path, cleanContent, resolvedSqlitePath);
|
|
865
891
|
if (stripped) {
|
|
866
892
|
const firstText = sqliteResult.content.find(
|
|
@@ -883,11 +909,13 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
883
909
|
await assertEditableFile(absolutePath, path);
|
|
884
910
|
}
|
|
885
911
|
|
|
912
|
+
const displayPath = formatPathRelativeToCwd(absolutePath, this.session.cwd);
|
|
913
|
+
emitWriteProgress(onUpdate, cleanContent, displayPath, absolutePath);
|
|
914
|
+
|
|
886
915
|
// Try ACP bridge first for editor-visible filesystem paths. Internal
|
|
887
916
|
// artifacts such as local:// plans are owned by OMP, not the editor.
|
|
888
917
|
if (await routeWriteThroughBridge(this.session, path, absolutePath, cleanContent)) {
|
|
889
918
|
const madeExecutable = await maybeMarkExecutableForShebang(absolutePath, cleanContent);
|
|
890
|
-
const displayPath = formatPathRelativeToCwd(absolutePath, this.session.cwd);
|
|
891
919
|
const header = maybeWriteSnapshotHeader(this.session, absolutePath, cleanContent);
|
|
892
920
|
const writeLine = `Successfully wrote ${cleanContent.length} bytes to ${displayPath}`;
|
|
893
921
|
let resultText = header ? `${header}\n${writeLine}` : writeLine;
|
|
@@ -908,7 +936,6 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
908
936
|
this.session.bumpFileMutationVersion?.(absolutePath);
|
|
909
937
|
const madeExecutable = await maybeMarkExecutableForShebang(absolutePath, cleanContent);
|
|
910
938
|
|
|
911
|
-
const displayPath = formatPathRelativeToCwd(absolutePath, this.session.cwd);
|
|
912
939
|
const header = maybeWriteSnapshotHeader(this.session, absolutePath, cleanContent);
|
|
913
940
|
const writeLine = `Successfully wrote ${cleanContent.length} bytes to ${displayPath}`;
|
|
914
941
|
let resultText = header ? `${header}\n${writeLine}` : writeLine;
|
|
@@ -1128,14 +1155,19 @@ export const writeToolRenderer = {
|
|
|
1128
1155
|
}));
|
|
1129
1156
|
}
|
|
1130
1157
|
|
|
1158
|
+
const isPartial = options.isPartial === true;
|
|
1159
|
+
const progressText = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1131
1160
|
const lineCount = countLines(fileContent);
|
|
1132
1161
|
const lineSuffix = formatLineCountSuffix(lineCount, uiTheme);
|
|
1133
|
-
const execSuffix =
|
|
1134
|
-
|
|
1135
|
-
|
|
1162
|
+
const execSuffix =
|
|
1163
|
+
!isPartial && result.details?.madeExecutable
|
|
1164
|
+
? `${uiTheme.fg("dim", " · ")}${uiTheme.fg("success", "made executable!")}`
|
|
1165
|
+
: "";
|
|
1136
1166
|
const header = renderStatusLine(
|
|
1137
1167
|
{
|
|
1138
|
-
|
|
1168
|
+
icon: isPartial ? "running" : undefined,
|
|
1169
|
+
iconOverride: isPartial ? undefined : uiTheme.styledSymbol("tool.write", "accent"),
|
|
1170
|
+
spinnerFrame: options.spinnerFrame,
|
|
1139
1171
|
title: "Write",
|
|
1140
1172
|
description: `${langIcon} ${pathDisplay}${lineSuffix}${execSuffix}`,
|
|
1141
1173
|
},
|
|
@@ -1147,7 +1179,15 @@ export const writeToolRenderer = {
|
|
|
1147
1179
|
return framedBlock(uiTheme, width => {
|
|
1148
1180
|
const { expanded } = options;
|
|
1149
1181
|
let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
|
|
1150
|
-
if (
|
|
1182
|
+
if (isPartial && progressText) {
|
|
1183
|
+
const safeProgressText = truncateToWidth(
|
|
1184
|
+
replaceTabs(progressText),
|
|
1185
|
+
TRUNCATE_LENGTHS.LINE,
|
|
1186
|
+
Ellipsis.Unicode,
|
|
1187
|
+
);
|
|
1188
|
+
body = `${uiTheme.fg("muted", safeProgressText)}${body ? `\n${body}` : ""}`;
|
|
1189
|
+
}
|
|
1190
|
+
if (!isPartial && diagnostics) {
|
|
1151
1191
|
const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
|
|
1152
1192
|
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
1153
1193
|
);
|
|
@@ -1162,7 +1202,7 @@ export const writeToolRenderer = {
|
|
|
1162
1202
|
return {
|
|
1163
1203
|
header,
|
|
1164
1204
|
sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
|
|
1165
|
-
state: "success",
|
|
1205
|
+
state: isPartial ? "pending" : "success",
|
|
1166
1206
|
borderColor: "borderMuted",
|
|
1167
1207
|
width,
|
|
1168
1208
|
};
|
|
@@ -55,7 +55,6 @@ function getSmolModelCandidates(
|
|
|
55
55
|
const configuredSmol = resolveModelRoleValue(settings.getModelRole("smol"), availableModels, {
|
|
56
56
|
settings,
|
|
57
57
|
matchPreferences,
|
|
58
|
-
modelRegistry: registry,
|
|
59
58
|
});
|
|
60
59
|
addCandidate(configuredSmol.model, configuredSmol.thinkingLevel);
|
|
61
60
|
|
|
@@ -39,8 +39,7 @@ const DESCRIPTION_UNAVAILABLE_NOTE =
|
|
|
39
39
|
"[Image description unavailable: the vision model returned no usable text. The image was saved for further analysis.]";
|
|
40
40
|
|
|
41
41
|
/** Registry surface needed to resolve a vision model and authorize requests. */
|
|
42
|
-
export type VisionFallbackRegistry = Pick<ModelRegistry, "getAvailable" | "getApiKey" | "resolver"
|
|
43
|
-
Partial<Pick<ModelRegistry, "resolveCanonicalModel" | "getCanonicalVariants" | "getCanonicalId">>;
|
|
42
|
+
export type VisionFallbackRegistry = Pick<ModelRegistry, "getAvailable" | "getApiKey" | "resolver">;
|
|
44
43
|
|
|
45
44
|
export interface DescribeAttachedImagesDeps {
|
|
46
45
|
/** Active (text-only) model the prompt is destined for. */
|
|
@@ -108,7 +107,7 @@ function resolveVisionModel(deps: DescribeAttachedImagesDeps): Model<Api> | unde
|
|
|
108
107
|
const resolvePattern = (pattern: string | undefined): Model<Api> | undefined => {
|
|
109
108
|
if (!pattern) return undefined;
|
|
110
109
|
const expanded = expandRoleAlias(pattern, deps.settings);
|
|
111
|
-
const model = resolveModelFromString(expanded, available, preferences
|
|
110
|
+
const model = resolveModelFromString(expanded, available, preferences);
|
|
112
111
|
return model?.input.includes("image") ? model : undefined;
|
|
113
112
|
};
|
|
114
113
|
return (
|
|
@@ -72,7 +72,7 @@ function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel
|
|
|
72
72
|
const availableModels = registry.getAvailable();
|
|
73
73
|
if (availableModels.length === 0) return undefined;
|
|
74
74
|
|
|
75
|
-
const titleModel = resolveRoleSelection(["tiny", "commit", "smol"], settings, availableModels
|
|
75
|
+
const titleModel = resolveRoleSelection(["tiny", "commit", "smol"], settings, availableModels)?.model;
|
|
76
76
|
if (titleModel) return titleModel;
|
|
77
77
|
|
|
78
78
|
if (currentModel) return currentModel;
|