@oh-my-pi/pi-coding-agent 15.13.1 → 15.13.3
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 +47 -0
- package/dist/cli.js +1057 -289
- package/dist/types/config/model-registry.d.ts +1 -0
- package/dist/types/config/models-config-schema.d.ts +3 -0
- package/dist/types/config/models-config.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +97 -0
- package/dist/types/edit/hashline/block-resolver.d.ts +1 -1
- package/dist/types/edit/index.d.ts +2 -0
- package/dist/types/eval/js/context-manager.d.ts +15 -0
- package/dist/types/modes/components/welcome.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +4 -4
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +2 -1
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/sdk.d.ts +3 -0
- package/dist/types/session/session-dump-format.d.ts +2 -1
- package/dist/types/session/unexpected-stop-classifier.d.ts +13 -0
- package/dist/types/stt/asr-client.d.ts +1 -1
- package/dist/types/system-prompt.d.ts +11 -0
- package/dist/types/tiny/title-client.d.ts +1 -1
- package/dist/types/tools/ask.d.ts +2 -0
- package/dist/types/tools/ast-edit.d.ts +2 -0
- package/dist/types/tools/ast-grep.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/debug.d.ts +2 -0
- package/dist/types/tools/eval.d.ts +2 -0
- package/dist/types/tools/find.d.ts +2 -0
- package/dist/types/tools/inspect-image.d.ts +2 -1
- package/dist/types/tools/irc.d.ts +2 -0
- package/dist/types/tools/job.d.ts +1 -0
- package/dist/types/tools/ssh.d.ts +2 -0
- package/dist/types/tools/todo.d.ts +2 -0
- package/dist/types/tts/tts-client.d.ts +1 -1
- package/dist/types/tui/tree-list.d.ts +1 -0
- package/dist/types/utils/thinking-display.d.ts +1 -17
- package/package.json +12 -12
- package/src/cli.ts +25 -12
- package/src/config/model-registry.ts +16 -2
- package/src/config/models-config-schema.ts +2 -0
- package/src/config/models-config.ts +1 -0
- package/src/config/settings-schema.ts +78 -0
- package/src/edit/hashline/block-resolver.ts +1 -1
- package/src/edit/hashline/execute.ts +1 -6
- package/src/edit/index.ts +48 -0
- package/src/eval/__tests__/agent-bridge.test.ts +106 -46
- package/src/eval/__tests__/js-context-manager.test.ts +53 -3
- package/src/eval/js/context-manager.ts +132 -29
- package/src/eval/js/worker-core.ts +1 -1
- package/src/eval/js/worker-entry.ts +7 -0
- package/src/export/html/template.js +18 -22
- package/src/internal-urls/docs-index.generated.ts +12 -3
- package/src/main.ts +15 -5
- package/src/modes/acp/acp-agent.ts +2 -2
- package/src/modes/acp/acp-event-mapper.ts +2 -2
- package/src/modes/components/agent-hub.ts +31 -7
- package/src/modes/components/assistant-message.ts +24 -15
- package/src/modes/components/snapcompact-shape-preview-doc.md +2 -2
- package/src/modes/components/snapcompact-shape-preview.ts +2 -2
- package/src/modes/components/tree-selector.ts +3 -2
- package/src/modes/components/welcome.ts +14 -4
- package/src/modes/controllers/event-controller.ts +3 -3
- package/src/modes/controllers/input-controller.ts +28 -39
- package/src/modes/controllers/streaming-reveal.ts +4 -4
- package/src/modes/interactive-mode.ts +2 -0
- package/src/modes/rpc/rpc-mode.ts +1 -0
- package/src/modes/rpc/rpc-types.ts +2 -2
- package/src/modes/types.ts +6 -0
- package/src/modes/utils/ui-helpers.ts +3 -3
- package/src/prompts/agents/oracle.md +0 -1
- package/src/prompts/agents/reviewer.md +0 -1
- package/src/prompts/system/system-prompt.md +17 -21
- package/src/prompts/system/unexpected-stop-classifier.md +17 -0
- package/src/prompts/system/unexpected-stop-retry.md +4 -0
- package/src/prompts/tools/ask.md +0 -8
- package/src/prompts/tools/ast-edit.md +0 -15
- package/src/prompts/tools/ast-grep.md +0 -13
- package/src/prompts/tools/browser.md +0 -21
- package/src/prompts/tools/debug.md +0 -13
- package/src/prompts/tools/eval.md +0 -9
- package/src/prompts/tools/find.md +0 -13
- package/src/prompts/tools/inspect-image.md +0 -9
- package/src/prompts/tools/irc.md +0 -15
- package/src/prompts/tools/patch.md +0 -13
- package/src/prompts/tools/ssh.md +0 -9
- package/src/prompts/tools/todo.md +1 -19
- package/src/sdk.ts +19 -0
- package/src/session/agent-session.ts +289 -29
- package/src/session/session-dump-format.ts +17 -49
- package/src/session/unexpected-stop-classifier.ts +129 -0
- package/src/stt/asr-client.ts +1 -1
- package/src/system-prompt.ts +31 -0
- package/src/tiny/title-client.ts +1 -1
- package/src/tools/ask.ts +41 -0
- package/src/tools/ast-edit.ts +46 -0
- package/src/tools/ast-grep.ts +24 -0
- package/src/tools/browser/tab-supervisor.ts +1 -1
- package/src/tools/browser/tab-worker-entry.ts +12 -4
- package/src/tools/browser.ts +52 -0
- package/src/tools/debug.ts +17 -0
- package/src/tools/eval.ts +20 -1
- package/src/tools/find.ts +24 -0
- package/src/tools/inspect-image.ts +27 -1
- package/src/tools/irc.ts +41 -0
- package/src/tools/job.ts +1 -0
- package/src/tools/ssh.ts +16 -0
- package/src/tools/todo.ts +82 -3
- package/src/tts/tts-client.ts +1 -1
- package/src/tui/tree-list.ts +68 -19
- package/src/utils/thinking-display.ts +8 -34
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "15.13.
|
|
4
|
+
"version": "15.13.3",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
"@agentclientprotocol/sdk": "0.25.0",
|
|
48
48
|
"@babel/parser": "^7.29.7",
|
|
49
49
|
"@mozilla/readability": "^0.6.0",
|
|
50
|
-
"@oh-my-pi/hashline": "15.13.
|
|
51
|
-
"@oh-my-pi/omp-stats": "15.13.
|
|
52
|
-
"@oh-my-pi/pi-agent-core": "15.13.
|
|
53
|
-
"@oh-my-pi/pi-ai": "15.13.
|
|
54
|
-
"@oh-my-pi/pi-catalog": "15.13.
|
|
55
|
-
"@oh-my-pi/pi-mnemopi": "15.13.
|
|
56
|
-
"@oh-my-pi/pi-natives": "15.13.
|
|
57
|
-
"@oh-my-pi/pi-tui": "15.13.
|
|
58
|
-
"@oh-my-pi/pi-utils": "15.13.
|
|
59
|
-
"@oh-my-pi/pi-wire": "15.13.
|
|
60
|
-
"@oh-my-pi/snapcompact": "15.13.
|
|
50
|
+
"@oh-my-pi/hashline": "15.13.3",
|
|
51
|
+
"@oh-my-pi/omp-stats": "15.13.3",
|
|
52
|
+
"@oh-my-pi/pi-agent-core": "15.13.3",
|
|
53
|
+
"@oh-my-pi/pi-ai": "15.13.3",
|
|
54
|
+
"@oh-my-pi/pi-catalog": "15.13.3",
|
|
55
|
+
"@oh-my-pi/pi-mnemopi": "15.13.3",
|
|
56
|
+
"@oh-my-pi/pi-natives": "15.13.3",
|
|
57
|
+
"@oh-my-pi/pi-tui": "15.13.3",
|
|
58
|
+
"@oh-my-pi/pi-utils": "15.13.3",
|
|
59
|
+
"@oh-my-pi/pi-wire": "15.13.3",
|
|
60
|
+
"@oh-my-pi/snapcompact": "15.13.3",
|
|
61
61
|
"@opentelemetry/api": "^1.9.1",
|
|
62
62
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
63
63
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
package/src/cli.ts
CHANGED
|
@@ -14,6 +14,7 @@ try {
|
|
|
14
14
|
* CLI entry point — registers all commands explicitly and delegates to the
|
|
15
15
|
* lightweight CLI runner from pi-utils.
|
|
16
16
|
*/
|
|
17
|
+
import { parentPort } from "node:worker_threads";
|
|
17
18
|
import type { CliConfig } from "@oh-my-pi/pi-utils/cli";
|
|
18
19
|
import {
|
|
19
20
|
APP_NAME,
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
setProfile,
|
|
24
25
|
VERSION,
|
|
25
26
|
} from "@oh-my-pi/pi-utils/dirs";
|
|
26
|
-
import { declareWorkerHostEntry } from "@oh-my-pi/pi-utils/worker-host";
|
|
27
|
+
import { declareWorkerHostEntry, installWorkerInbox } from "@oh-my-pi/pi-utils/worker-host";
|
|
27
28
|
import { installProfileAlias, resolveProfileAliasCommandFromProcess } from "./cli/profile-alias";
|
|
28
29
|
import { extractProfileFlags } from "./cli/profile-bootstrap";
|
|
29
30
|
|
|
@@ -67,6 +68,7 @@ async function runSmokeTest(): Promise<void> {
|
|
|
67
68
|
const { smokeTestTinyTitleWorker } = await import("./tiny/title-client");
|
|
68
69
|
const { smokeTestSttWorker } = await import("./stt/asr-client");
|
|
69
70
|
const { smokeTestTtsWorker } = await import("./tts/tts-client");
|
|
71
|
+
const { smokeTestJsEvalWorker } = await import("./eval/js/context-manager");
|
|
70
72
|
await smokeTestSyncWorker();
|
|
71
73
|
|
|
72
74
|
const statsServer = await startServer(0);
|
|
@@ -83,18 +85,23 @@ async function runSmokeTest(): Promise<void> {
|
|
|
83
85
|
|
|
84
86
|
await smokeTestTinyTitleWorker();
|
|
85
87
|
await smokeTestSttWorker();
|
|
88
|
+
await smokeTestJsEvalWorker();
|
|
86
89
|
await smokeTestTtsWorker();
|
|
87
90
|
process.stdout.write("smoke-test: ok\n");
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
const
|
|
91
|
-
const STATS_SYNC_WORKER_ARG = "
|
|
92
|
-
const TAB_WORKER_ARG = "
|
|
93
|
-
const JS_EVAL_WORKER_ARG = "
|
|
94
|
-
const STT_WORKER_ARG = "
|
|
95
|
-
const TTS_WORKER_ARG = "
|
|
93
|
+
const TINY_WORKER_ARG = "__omp_worker_tiny_inference";
|
|
94
|
+
const STATS_SYNC_WORKER_ARG = "__omp_worker_stats_sync";
|
|
95
|
+
const TAB_WORKER_ARG = "__omp_worker_tab";
|
|
96
|
+
const JS_EVAL_WORKER_ARG = "__omp_worker_js_eval";
|
|
97
|
+
const STT_WORKER_ARG = "__omp_worker_stt";
|
|
98
|
+
const TTS_WORKER_ARG = "__omp_worker_tts";
|
|
96
99
|
|
|
97
100
|
async function runWorkerEntrypoint(arg: string | undefined): Promise<boolean> {
|
|
101
|
+
if (arg === TINY_WORKER_ARG) {
|
|
102
|
+
await runTinyWorker();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
98
105
|
if (arg === STATS_SYNC_WORKER_ARG) {
|
|
99
106
|
// The sync worker handles messages via `self.onmessage`, assigned during
|
|
100
107
|
// this *async* dynamic import. Bun flushes the worker's initial message
|
|
@@ -117,11 +124,20 @@ async function runWorkerEntrypoint(arg: string | undefined): Promise<boolean> {
|
|
|
117
124
|
}
|
|
118
125
|
return true;
|
|
119
126
|
}
|
|
127
|
+
// Bun flushes messages the parent posted before spawn once this entry's
|
|
128
|
+
// top-level evaluation completes, delivering them only to listeners present
|
|
129
|
+
// at that moment. These worker modules are imported dynamically below, so
|
|
130
|
+
// their own `parentPort.on("message")` lands after the flush and the parent's
|
|
131
|
+
// synchronous `init` is dropped. Install a buffering inbox synchronously here
|
|
132
|
+
// (still inside the entry's sync prefix) so the handshake survives; the worker
|
|
133
|
+
// module binds the real handler once loaded.
|
|
120
134
|
if (arg === TAB_WORKER_ARG) {
|
|
135
|
+
if (parentPort) installWorkerInbox(parentPort);
|
|
121
136
|
await import("./tools/browser/tab-worker-entry");
|
|
122
137
|
return true;
|
|
123
138
|
}
|
|
124
139
|
if (arg === JS_EVAL_WORKER_ARG) {
|
|
140
|
+
if (parentPort) installWorkerInbox(parentPort);
|
|
125
141
|
await import("./eval/js/worker-entry");
|
|
126
142
|
return true;
|
|
127
143
|
}
|
|
@@ -251,11 +267,8 @@ export async function runCli(argv: string[]): Promise<void> {
|
|
|
251
267
|
// synchronous prefix of `runWorkerEntrypoint`, and Bun flushes the
|
|
252
268
|
// worker's parked initial messages as soon as the entry module's
|
|
253
269
|
// top-level evaluation finishes.
|
|
254
|
-
if (
|
|
255
|
-
await
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
if (await runWorkerEntrypoint(resolvedArgv[0])) {
|
|
270
|
+
if (resolvedArgv[0]?.startsWith("__omp_worker_")) {
|
|
271
|
+
await runWorkerEntrypoint(resolvedArgv[0]);
|
|
259
272
|
return;
|
|
260
273
|
}
|
|
261
274
|
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
resolveCanonicalVariant,
|
|
60
60
|
resolveModelReference,
|
|
61
61
|
} from "@oh-my-pi/pi-catalog/identity";
|
|
62
|
-
import { isRecord, logger } from "@oh-my-pi/pi-utils";
|
|
62
|
+
import { isBunTestRuntime, isRecord, logger } from "@oh-my-pi/pi-utils";
|
|
63
63
|
import { parseModelString, resolveProviderModelReference } from "../config/model-resolver";
|
|
64
64
|
import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
|
65
65
|
import { type ApiKeyResolverModel, type ApiKeyResolverOptions, createApiKeyResolver } from "./api-key-resolver";
|
|
@@ -130,11 +130,13 @@ export function mergeDiscoveredModel<TApi extends Api>(
|
|
|
130
130
|
providerOverride?: Pick<ProviderOverride, "baseUrl" | "headers" | "transport">,
|
|
131
131
|
): Model<TApi> {
|
|
132
132
|
if (existing) {
|
|
133
|
+
const supportsTools = model.supportsTools ?? existing.supportsTools;
|
|
133
134
|
return buildModel({
|
|
134
135
|
...model,
|
|
135
136
|
baseUrl: providerOverride?.baseUrl ?? model.baseUrl ?? existing.baseUrl,
|
|
136
137
|
headers: existing.headers ? { ...existing.headers, ...model.headers } : model.headers,
|
|
137
138
|
transport: providerOverride?.transport ?? existing.transport ?? model.transport,
|
|
139
|
+
...(supportsTools !== undefined ? { supportsTools } : {}),
|
|
138
140
|
compat: model.compatConfig,
|
|
139
141
|
} as ModelSpec<TApi>);
|
|
140
142
|
}
|
|
@@ -370,6 +372,7 @@ interface ModelPatch {
|
|
|
370
372
|
reasoning?: boolean;
|
|
371
373
|
thinking?: ThinkingConfig;
|
|
372
374
|
input?: ("text" | "image")[];
|
|
375
|
+
supportsTools?: boolean;
|
|
373
376
|
cost?: Partial<Model<Api>["cost"]>;
|
|
374
377
|
contextWindow?: number;
|
|
375
378
|
maxTokens?: number;
|
|
@@ -395,6 +398,7 @@ function applyModelPatch(base: Model<Api>, patch: ModelPatch, transport: ModelTr
|
|
|
395
398
|
if (patch.reasoning !== undefined) result.reasoning = patch.reasoning;
|
|
396
399
|
if (patch.thinking !== undefined) result.thinking = patch.thinking;
|
|
397
400
|
if (patch.input !== undefined) result.input = patch.input;
|
|
401
|
+
if (patch.supportsTools !== undefined) result.supportsTools = patch.supportsTools;
|
|
398
402
|
if (patch.contextWindow !== undefined) result.contextWindow = patch.contextWindow;
|
|
399
403
|
if (patch.maxTokens !== undefined) result.maxTokens = patch.maxTokens;
|
|
400
404
|
if (patch.omitMaxOutputTokens !== undefined) result.omitMaxOutputTokens = patch.omitMaxOutputTokens;
|
|
@@ -506,6 +510,7 @@ function buildCustomModelOverlay(
|
|
|
506
510
|
reasoning: modelDef.reasoning,
|
|
507
511
|
thinking: modelDef.thinking,
|
|
508
512
|
input: modelDef.input,
|
|
513
|
+
supportsTools: modelDef.supportsTools,
|
|
509
514
|
cost: modelDef.cost,
|
|
510
515
|
contextWindow: modelDef.contextWindow,
|
|
511
516
|
maxTokens: modelDef.maxTokens,
|
|
@@ -535,6 +540,7 @@ function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuil
|
|
|
535
540
|
reference?.cost ??
|
|
536
541
|
(options.useDefaults ? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } : undefined);
|
|
537
542
|
const input = resolvedModel.input ?? reference?.input ?? (options.useDefaults ? ["text"] : undefined);
|
|
543
|
+
const supportsTools = resolvedModel.supportsTools ?? reference?.supportsTools;
|
|
538
544
|
return buildModel({
|
|
539
545
|
id: resolvedModel.id,
|
|
540
546
|
name: resolvedModel.name ?? (options.useDefaults ? resolvedModel.id : undefined),
|
|
@@ -544,6 +550,7 @@ function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuil
|
|
|
544
550
|
reasoning: resolvedModel.reasoning ?? reference?.reasoning ?? (options.useDefaults ? false : undefined),
|
|
545
551
|
thinking: resolvedModel.thinking ?? reference?.thinking,
|
|
546
552
|
input: input as ("text" | "image")[],
|
|
553
|
+
...(supportsTools !== undefined ? { supportsTools } : {}),
|
|
547
554
|
cost,
|
|
548
555
|
contextWindow: resolvedModel.contextWindow ?? reference?.contextWindow ?? (options.useDefaults ? 128000 : null),
|
|
549
556
|
maxTokens: resolvedModel.maxTokens ?? reference?.maxTokens ?? (options.useDefaults ? 16384 : null),
|
|
@@ -683,7 +690,11 @@ export class ModelRegistry {
|
|
|
683
690
|
modelsPath?: string,
|
|
684
691
|
options?: { fetch?: FetchImpl },
|
|
685
692
|
) {
|
|
686
|
-
this.#fetch =
|
|
693
|
+
this.#fetch =
|
|
694
|
+
options?.fetch ??
|
|
695
|
+
(isBunTestRuntime()
|
|
696
|
+
? () => Promise.reject(new Error("network disabled in model-registry runtime test"))
|
|
697
|
+
: fetch);
|
|
687
698
|
this.#modelsConfigFile = ModelsConfigFile.relocate(modelsPath);
|
|
688
699
|
this.#cacheDbPath = modelsPath ? path.join(path.dirname(modelsPath), "models.db") : undefined;
|
|
689
700
|
// Set up fallback resolver for custom provider API keys
|
|
@@ -878,10 +889,12 @@ export class ModelRegistry {
|
|
|
878
889
|
#mergeResolvedModels(baseModels: Model<Api>[], replacementModels: Model<Api>[]): Model<Api>[] {
|
|
879
890
|
return mergeByModelKey(baseModels, replacementModels, (existing, replacementModel) => {
|
|
880
891
|
if (!existing) return replacementModel;
|
|
892
|
+
const supportsTools = replacementModel.supportsTools ?? existing.supportsTools;
|
|
881
893
|
return {
|
|
882
894
|
...replacementModel,
|
|
883
895
|
contextWindow: replacementModel.contextWindow ?? existing.contextWindow,
|
|
884
896
|
maxTokens: replacementModel.maxTokens ?? existing.maxTokens,
|
|
897
|
+
...(supportsTools !== undefined ? { supportsTools } : {}),
|
|
885
898
|
};
|
|
886
899
|
});
|
|
887
900
|
}
|
|
@@ -2205,6 +2218,7 @@ export interface ProviderConfigInput {
|
|
|
2205
2218
|
reasoning: boolean;
|
|
2206
2219
|
thinking?: ThinkingConfig;
|
|
2207
2220
|
input: ("text" | "image")[];
|
|
2221
|
+
supportsTools?: boolean;
|
|
2208
2222
|
cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
2209
2223
|
contextWindow: number;
|
|
2210
2224
|
maxTokens: number;
|
|
@@ -133,6 +133,7 @@ const ModelDefinitionSchema = z.object({
|
|
|
133
133
|
reasoning: z.boolean().optional(),
|
|
134
134
|
thinking: ModelThinkingSchema.optional(),
|
|
135
135
|
input: z.array(z.enum(["text", "image"])).optional(),
|
|
136
|
+
supportsTools: z.boolean().optional(),
|
|
136
137
|
cost: z
|
|
137
138
|
.object({
|
|
138
139
|
input: z.number(),
|
|
@@ -155,6 +156,7 @@ export const ModelOverrideSchema = z.object({
|
|
|
155
156
|
reasoning: z.boolean().optional(),
|
|
156
157
|
thinking: ModelThinkingSchema.optional(),
|
|
157
158
|
input: z.array(z.enum(["text", "image"])).optional(),
|
|
159
|
+
supportsTools: z.boolean().optional(),
|
|
158
160
|
cost: z
|
|
159
161
|
.object({
|
|
160
162
|
input: z.number().optional(),
|
|
@@ -116,6 +116,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
|
|
|
116
116
|
"Magic Keywords",
|
|
117
117
|
"Startup & Updates",
|
|
118
118
|
"Power (macOS)",
|
|
119
|
+
"Agent",
|
|
119
120
|
],
|
|
120
121
|
context: ["General", "Compaction", "Rules (TTSR)", "Experimental"],
|
|
121
122
|
memory: ["General", "Auto-Learn", "Mnemopi", "Hindsight"],
|
|
@@ -1719,6 +1720,48 @@ export const SETTINGS_SCHEMA = {
|
|
|
1719
1720
|
},
|
|
1720
1721
|
},
|
|
1721
1722
|
|
|
1723
|
+
"tools.format": {
|
|
1724
|
+
type: "enum",
|
|
1725
|
+
values: [
|
|
1726
|
+
"auto",
|
|
1727
|
+
"native",
|
|
1728
|
+
"glm",
|
|
1729
|
+
"hermes",
|
|
1730
|
+
"kimi",
|
|
1731
|
+
"xml",
|
|
1732
|
+
"anthropic",
|
|
1733
|
+
"deepseek",
|
|
1734
|
+
"harmony",
|
|
1735
|
+
"pi",
|
|
1736
|
+
"qwen3",
|
|
1737
|
+
] as const,
|
|
1738
|
+
default: "auto",
|
|
1739
|
+
ui: {
|
|
1740
|
+
tab: "context",
|
|
1741
|
+
group: "Experimental",
|
|
1742
|
+
label: "Tool Call Format",
|
|
1743
|
+
description:
|
|
1744
|
+
"Controls how tools are exposed to the model. Auto uses native tool calls unless the selected model is marked as not supporting tools, then falls back to GLM-style in-band tool calls. Native forces provider-native tools; the other values force the named in-band syntax. Applies on session start.",
|
|
1745
|
+
options: [
|
|
1746
|
+
{
|
|
1747
|
+
value: "auto",
|
|
1748
|
+
label: "Auto",
|
|
1749
|
+
description: "Use native tool calls unless the model is known not to support them.",
|
|
1750
|
+
},
|
|
1751
|
+
{ value: "native", label: "Native", description: "Use provider-native tool calls." },
|
|
1752
|
+
{ value: "glm", label: "GLM", description: "Use GLM-style in-band tool calls." },
|
|
1753
|
+
{ value: "hermes", label: "Hermes", description: "Use Hermes-style in-band tool calls." },
|
|
1754
|
+
{ value: "kimi", label: "Kimi", description: "Use Kimi-style in-band tool calls." },
|
|
1755
|
+
{ value: "xml", label: "XML", description: "Use generic XML in-band tool calls." },
|
|
1756
|
+
{ value: "anthropic", label: "Anthropic", description: "Use Anthropic-style in-band tool calls." },
|
|
1757
|
+
{ value: "deepseek", label: "DeepSeek", description: "Use DeepSeek-style in-band tool calls." },
|
|
1758
|
+
{ value: "harmony", label: "Harmony", description: "Use Harmony-style in-band tool calls." },
|
|
1759
|
+
{ value: "pi", label: "Pi", description: "Use Pi-style in-band tool calls." },
|
|
1760
|
+
{ value: "qwen3", label: "Qwen3", description: "Use Qwen3-style in-band tool calls." },
|
|
1761
|
+
],
|
|
1762
|
+
},
|
|
1763
|
+
},
|
|
1764
|
+
|
|
1722
1765
|
"snapcompact.shape": {
|
|
1723
1766
|
type: "enum",
|
|
1724
1767
|
values: ["auto", ...SHAPE_VARIANT_NAMES] as const,
|
|
@@ -3183,6 +3226,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
3183
3226
|
description: "Ask the agent to describe the intent of each tool call before executing it",
|
|
3184
3227
|
},
|
|
3185
3228
|
},
|
|
3229
|
+
"tools.abortOnFabricatedResult": {
|
|
3230
|
+
type: "boolean",
|
|
3231
|
+
default: true,
|
|
3232
|
+
ui: {
|
|
3233
|
+
tab: "tools",
|
|
3234
|
+
group: "Execution",
|
|
3235
|
+
label: "Abort On Fabricated Tool Result",
|
|
3236
|
+
description:
|
|
3237
|
+
"With in-band tool calls, stop the model immediately when it starts hallucinating a tool result mid-turn. Disable to let the model finish generating and discard the fabricated continuation instead.",
|
|
3238
|
+
},
|
|
3239
|
+
},
|
|
3186
3240
|
|
|
3187
3241
|
"tools.maxTimeout": {
|
|
3188
3242
|
type: "number",
|
|
@@ -3940,6 +3994,30 @@ export const SETTINGS_SCHEMA = {
|
|
|
3940
3994
|
options: AUTO_THINKING_MODEL_OPTIONS,
|
|
3941
3995
|
},
|
|
3942
3996
|
},
|
|
3997
|
+
"features.unexpectedStopDetection": {
|
|
3998
|
+
type: "boolean",
|
|
3999
|
+
default: false,
|
|
4000
|
+
ui: {
|
|
4001
|
+
tab: "interaction",
|
|
4002
|
+
group: "Agent",
|
|
4003
|
+
label: "Detect unexpected stops",
|
|
4004
|
+
description:
|
|
4005
|
+
"Use a small model to detect when the assistant says it will continue but stops without tool calls; automatically prompt it to continue.",
|
|
4006
|
+
},
|
|
4007
|
+
},
|
|
4008
|
+
"providers.unexpectedStopModel": {
|
|
4009
|
+
type: "enum",
|
|
4010
|
+
values: TINY_MEMORY_MODEL_VALUES,
|
|
4011
|
+
default: ONLINE_MEMORY_MODEL_KEY,
|
|
4012
|
+
ui: {
|
|
4013
|
+
tab: "providers",
|
|
4014
|
+
group: "Tiny Model",
|
|
4015
|
+
label: "Unexpected Stop Model",
|
|
4016
|
+
description: "Classifier for unexpected-stop detection: online smol by default, or a local on-device model.",
|
|
4017
|
+
condition: "unexpectedStopDetection",
|
|
4018
|
+
options: TINY_MEMORY_MODEL_OPTIONS,
|
|
4019
|
+
},
|
|
4020
|
+
},
|
|
3943
4021
|
|
|
3944
4022
|
"providers.kimiApiFormat": {
|
|
3945
4023
|
type: "enum",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tree-sitter-backed {@link BlockResolver} for the hashline
|
|
2
|
+
* Tree-sitter-backed {@link BlockResolver} for the hashline block replace
|
|
3
3
|
* operator. Bridges the pure hashline seam to the native `blockRangeAt`
|
|
4
4
|
* primitive in `@oh-my-pi/pi-natives`, which infers the language from the file
|
|
5
5
|
* path and returns the 1-indexed line span of the syntactic block beginning on
|
|
@@ -98,12 +98,7 @@ interface RenderedSection {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
function formatBlockResolution(resolution: BlockResolution): string {
|
|
101
|
-
const op =
|
|
102
|
-
resolution.op === "delete"
|
|
103
|
-
? "delete block"
|
|
104
|
-
: resolution.op === "insert_after"
|
|
105
|
-
? "insert after block"
|
|
106
|
-
: "replace block";
|
|
101
|
+
const op = resolution.op === "delete" ? "DEL.BLK" : resolution.op === "insert_after" ? "INS.BLK.POST" : "SWAP.BLK";
|
|
107
102
|
const lines = resolution.end - resolution.start + 1;
|
|
108
103
|
const span =
|
|
109
104
|
resolution.start === resolution.end ? `line ${resolution.start}` : `lines ${resolution.start}-${resolution.end}`;
|
package/src/edit/index.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { MismatchError as HashlineMismatchError } from "@oh-my-pi/hashline";
|
|
|
2
2
|
import hashlineGrammar from "@oh-my-pi/hashline/grammar.lark" with { type: "text" };
|
|
3
3
|
import hashlineDescription from "@oh-my-pi/hashline/prompt.md" with { type: "text" };
|
|
4
4
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
5
|
+
import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
5
6
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import type { z } from "zod/v4";
|
|
6
8
|
import {
|
|
7
9
|
createLspWritethrough,
|
|
8
10
|
type FileDiagnosticsResult,
|
|
@@ -50,6 +52,7 @@ type EditParams = ReplaceParams | PatchParams | HashlineParams | ApplyPatchParam
|
|
|
50
52
|
type EditModeDefinition = {
|
|
51
53
|
description: (session: ToolSession) => string;
|
|
52
54
|
parameters: TInput;
|
|
55
|
+
examples?: readonly ToolExample[];
|
|
53
56
|
execute: (
|
|
54
57
|
tool: EditTool,
|
|
55
58
|
params: EditParams,
|
|
@@ -356,6 +359,10 @@ export class EditTool implements AgentTool<TInput> {
|
|
|
356
359
|
return this.#getModeDefinition().parameters;
|
|
357
360
|
}
|
|
358
361
|
|
|
362
|
+
get examples(): readonly ToolExample[] | undefined {
|
|
363
|
+
return this.#getModeDefinition().examples;
|
|
364
|
+
}
|
|
365
|
+
|
|
359
366
|
/**
|
|
360
367
|
* When in `apply_patch` mode, expose the Codex Lark grammar so providers
|
|
361
368
|
* that support OpenAI-style custom tools can emit a grammar-constrained
|
|
@@ -404,6 +411,39 @@ export class EditTool implements AgentTool<TInput> {
|
|
|
404
411
|
patch: {
|
|
405
412
|
description: () => prompt.render(patchDescription),
|
|
406
413
|
parameters: patchEditSchema,
|
|
414
|
+
examples: [
|
|
415
|
+
{
|
|
416
|
+
caption: "Create",
|
|
417
|
+
call: { path: "hello.txt", edits: [{ op: "create", diff: "Hello\n" }] },
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
caption: "Update",
|
|
421
|
+
call: {
|
|
422
|
+
path: "src/app.py",
|
|
423
|
+
edits: [
|
|
424
|
+
{
|
|
425
|
+
op: "update",
|
|
426
|
+
diff: "@@ def greet():\n def greet():\n-print('Hi')\n+print('Hello')\n",
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
caption: "Rename",
|
|
433
|
+
call: {
|
|
434
|
+
path: "src/app.py",
|
|
435
|
+
edits: [{ op: "update", rename: "src/main.py", diff: "@@\n …\n" }],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
caption: "Delete",
|
|
440
|
+
call: { path: "obsolete.txt", edits: [{ op: "delete" }] },
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
caption: "Multiple entries",
|
|
444
|
+
note: "All entries in one call apply to the top-level `path`; use separate calls for different files.",
|
|
445
|
+
},
|
|
446
|
+
] satisfies readonly ToolExample<z.input<typeof patchEditSchema>>[],
|
|
407
447
|
execute: (
|
|
408
448
|
tool: EditTool,
|
|
409
449
|
params: EditParams,
|
|
@@ -432,6 +472,14 @@ export class EditTool implements AgentTool<TInput> {
|
|
|
432
472
|
apply_patch: {
|
|
433
473
|
description: () => prompt.render(applyPatchDescription),
|
|
434
474
|
parameters: applyPatchSchema,
|
|
475
|
+
examples: [
|
|
476
|
+
{
|
|
477
|
+
caption: "Apply a combined patch file",
|
|
478
|
+
call: {
|
|
479
|
+
input: '*** Begin Patch\n*** Add File: hello.txt\n+Hello world\n*** Update File: src/app.py\n*** Move to: src/main.py\n@@ def greet():\n-print("Hi")\n+print("Hello, world!")\n*** Delete File: obsolete.txt\n*** End Patch\n',
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
] satisfies readonly ToolExample<z.input<typeof applyPatchSchema>>[],
|
|
435
483
|
execute: (
|
|
436
484
|
tool: EditTool,
|
|
437
485
|
params: EditParams,
|