@oh-my-pi/pi-coding-agent 16.2.6 → 16.2.8
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 +46 -0
- package/dist/cli.js +2781 -2660
- package/dist/types/cli/bench-cli.d.ts +3 -3
- package/dist/types/commands/bench.d.ts +1 -1
- package/dist/types/config/service-tier.d.ts +39 -24
- package/dist/types/config/settings-schema.d.ts +92 -36
- package/dist/types/edit/hashline/filesystem.d.ts +1 -0
- package/dist/types/mcp/config-writer.d.ts +48 -0
- package/dist/types/mcp/types.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +33 -13
- package/dist/types/session/messages.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -7
- package/dist/types/session/session-entries.d.ts +2 -2
- package/dist/types/session/session-manager.d.ts +2 -2
- package/dist/types/session/settings-stream-fn.d.ts +2 -2
- package/dist/types/system-prompt.test.d.ts +1 -0
- package/dist/types/task/executor.d.ts +6 -6
- package/dist/types/task/types.d.ts +6 -0
- package/dist/types/task/worktree.d.ts +32 -6
- package/dist/types/tiny/title-client.d.ts +5 -1
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tools/output-schema-validator.d.ts +10 -0
- package/dist/types/utils/git.d.ts +17 -0
- package/package.json +12 -12
- package/src/cli/bench-cli.ts +19 -12
- package/src/cli/tiny-models-cli.ts +18 -4
- package/src/commands/bench.ts +3 -3
- package/src/config/mcp-schema.json +10 -1
- package/src/config/service-tier.ts +85 -56
- package/src/config/settings-schema.ts +81 -37
- package/src/config/settings.ts +47 -0
- package/src/discovery/builtin-rules/go-add-cleanup.md +32 -0
- package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
- package/src/discovery/builtin-rules/go-exp-promoted.md +39 -0
- package/src/discovery/builtin-rules/go-ioutil.md +36 -0
- package/src/discovery/builtin-rules/go-join-hostport.md +29 -0
- package/src/discovery/builtin-rules/go-new-expr.md +44 -0
- package/src/discovery/builtin-rules/go-rand-v2.md +40 -0
- package/src/discovery/builtin-rules/go-range-int.md +45 -0
- package/src/discovery/builtin-rules/index.ts +16 -0
- package/src/edit/hashline/filesystem.ts +12 -0
- package/src/eval/agent-bridge.ts +4 -2
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/config-writer.ts +121 -0
- package/src/mcp/config.ts +10 -6
- package/src/mcp/types.ts +3 -0
- package/src/modes/components/extensions/extension-dashboard.ts +46 -0
- package/src/modes/components/extensions/state-manager.ts +24 -3
- package/src/modes/controllers/event-controller.ts +7 -0
- package/src/modes/controllers/tool-args-reveal.ts +1 -1
- package/src/modes/utils/transcript-render-helpers.ts +2 -2
- package/src/prompts/tools/bash.md +1 -4
- package/src/sdk.ts +12 -11
- package/src/session/agent-session.ts +294 -82
- package/src/session/messages.ts +1 -1
- package/src/session/session-context.ts +17 -6
- package/src/session/session-entries.ts +2 -2
- package/src/session/session-manager.ts +9 -2
- package/src/session/settings-stream-fn.ts +12 -2
- package/src/slash-commands/builtin-registry.ts +2 -10
- package/src/system-prompt.test.ts +158 -0
- package/src/system-prompt.ts +69 -26
- package/src/task/executor.ts +23 -16
- package/src/task/index.ts +7 -5
- package/src/task/isolation-runner.ts +15 -1
- package/src/task/types.ts +6 -0
- package/src/task/worktree.ts +219 -38
- package/src/tiny/title-client.ts +19 -13
- package/src/tools/grep.ts +19 -1
- package/src/tools/index.ts +3 -3
- package/src/tools/irc.ts +9 -3
- package/src/tools/output-schema-validator.ts +38 -0
- package/src/tools/read.ts +28 -28
- package/src/tools/yield.ts +52 -15
- package/src/utils/file-mentions.ts +10 -1
- package/src/utils/git.ts +38 -0
- package/src/web/search/providers/duckduckgo.ts +17 -3
package/src/tools/yield.ts
CHANGED
|
@@ -98,6 +98,16 @@ function parseYieldType(value: unknown): string | string[] | undefined {
|
|
|
98
98
|
throw new Error("type must be a string or non-empty array of strings");
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Render an incremental yield's `type: [...]` labels as a quoted, comma-separated list for
|
|
103
|
+
* model-facing retry messages — keeps the failed section labelled even when the yield carried
|
|
104
|
+
* multiple labels at once.
|
|
105
|
+
*/
|
|
106
|
+
function formatYieldLabels(labels: readonly string[]): string {
|
|
107
|
+
if (labels.length === 0) return '""';
|
|
108
|
+
return labels.map(label => `"${label}"`).join(", ");
|
|
109
|
+
}
|
|
110
|
+
|
|
101
111
|
/**
|
|
102
112
|
* Expand a plain-object `data` schema into a strict union that ALSO accepts each
|
|
103
113
|
* top-level section value (and array element) on its own. Agents that yield
|
|
@@ -202,10 +212,12 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
202
212
|
lenientArgValidation = true;
|
|
203
213
|
|
|
204
214
|
readonly #validate?: (value: unknown) => JsonSchemaValidationResult;
|
|
215
|
+
readonly #validateSection?: ReadonlyMap<string, (value: unknown) => JsonSchemaValidationResult>;
|
|
205
216
|
#schemaValidationFailures = 0;
|
|
206
217
|
|
|
207
218
|
constructor(session: ToolSession) {
|
|
208
219
|
let validate: ((value: unknown) => JsonSchemaValidationResult) | undefined;
|
|
220
|
+
let validateSection: ReadonlyMap<string, (value: unknown) => JsonSchemaValidationResult> | undefined;
|
|
209
221
|
let parameters: TSchema;
|
|
210
222
|
|
|
211
223
|
try {
|
|
@@ -217,6 +229,7 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
217
229
|
} = buildOutputValidator(session.outputSchema);
|
|
218
230
|
if (validator) {
|
|
219
231
|
validate = value => validator.validate(value);
|
|
232
|
+
validateSection = validator.validateSection;
|
|
220
233
|
}
|
|
221
234
|
|
|
222
235
|
const schemaHint = formatSchema(normalizedSchema ?? session.outputSchema);
|
|
@@ -266,6 +279,7 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
266
279
|
}
|
|
267
280
|
|
|
268
281
|
this.#validate = validate;
|
|
282
|
+
this.#validateSection = validateSection;
|
|
269
283
|
this.parameters = parameters;
|
|
270
284
|
}
|
|
271
285
|
|
|
@@ -307,22 +321,25 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
307
321
|
if (data === null) {
|
|
308
322
|
throw new Error("data is required when yield indicates success");
|
|
309
323
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
this.#
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
`
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
324
|
+
const sectionFailure = isIncremental
|
|
325
|
+
? this.#validateIncrementalSection(yieldType as string[], data)
|
|
326
|
+
: this.#validate
|
|
327
|
+
? this.#validate(data)
|
|
328
|
+
: undefined;
|
|
329
|
+
if (sectionFailure && !sectionFailure.success) {
|
|
330
|
+
this.#schemaValidationFailures++;
|
|
331
|
+
if (this.#schemaValidationFailures <= MAX_SCHEMA_RETRIES) {
|
|
332
|
+
const remaining = MAX_SCHEMA_RETRIES - this.#schemaValidationFailures;
|
|
333
|
+
const retryHint =
|
|
334
|
+
remaining > 0
|
|
335
|
+
? ` Call yield again with the corrected shape — ${remaining} retry attempt(s) remain before the schema constraint is dropped.`
|
|
336
|
+
: " Call yield again with the corrected shape — this is the final retry before the schema constraint is dropped.";
|
|
337
|
+
const scope = isIncremental ? `Section ${formatYieldLabels(yieldType as string[])}` : "Output";
|
|
338
|
+
throw new Error(
|
|
339
|
+
`${scope} does not match schema: ${formatAllValidationIssues(sectionFailure.issues)}.${retryHint}`,
|
|
340
|
+
);
|
|
325
341
|
}
|
|
342
|
+
schemaValidationOverridden = true;
|
|
326
343
|
}
|
|
327
344
|
}
|
|
328
345
|
|
|
@@ -344,6 +361,26 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
344
361
|
},
|
|
345
362
|
};
|
|
346
363
|
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Validate the `data` payload of an incremental yield (`type: ["<label>", …]`) against
|
|
367
|
+
* the matching property's sub-validator. Returns the first failure across all known labels,
|
|
368
|
+
* or `undefined` when no label is recognised (user-defined section labels stay loose) or
|
|
369
|
+
* when all known labels accept the value. Lets the model see the same retry feedback that
|
|
370
|
+
* the terminal-yield path already produces, instead of leaking the mismatch through to
|
|
371
|
+
* the parent's post-mortem `schema_violation`.
|
|
372
|
+
*/
|
|
373
|
+
#validateIncrementalSection(labels: string[], data: unknown): JsonSchemaValidationResult | undefined {
|
|
374
|
+
const subValidators = this.#validateSection;
|
|
375
|
+
if (!subValidators || subValidators.size === 0) return undefined;
|
|
376
|
+
for (const label of labels) {
|
|
377
|
+
const sub = subValidators.get(label);
|
|
378
|
+
if (!sub) continue;
|
|
379
|
+
const parsed = sub(data);
|
|
380
|
+
if (!parsed.success) return parsed;
|
|
381
|
+
}
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
347
384
|
}
|
|
348
385
|
|
|
349
386
|
// Register subprocess tool handler for extraction + termination.
|
|
@@ -10,7 +10,7 @@ import path from "node:path";
|
|
|
10
10
|
import { formatHashlineHeader, formatNumberedLines, type SnapshotStore } from "@oh-my-pi/hashline";
|
|
11
11
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
12
12
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
13
|
-
import { formatAge, formatBytes, readImageMetadata } from "@oh-my-pi/pi-utils";
|
|
13
|
+
import { formatAge, formatBytes, isProbablyBinary, readImageMetadata } from "@oh-my-pi/pi-utils";
|
|
14
14
|
import { canonicalSnapshotKey } from "../edit/file-snapshot-store";
|
|
15
15
|
import { normalizeToLF } from "../edit/normalize";
|
|
16
16
|
import type { FileMentionMessage } from "../session/messages";
|
|
@@ -257,6 +257,15 @@ export async function generateFileMentionMessages(
|
|
|
257
257
|
});
|
|
258
258
|
continue;
|
|
259
259
|
}
|
|
260
|
+
if (await isProbablyBinary(absolutePath)) {
|
|
261
|
+
files.push({
|
|
262
|
+
path: resolvedPath,
|
|
263
|
+
content: `(skipped auto-read: binary file, ${formatBytes(stat.size)})`,
|
|
264
|
+
byteSize: stat.size,
|
|
265
|
+
skippedReason: "binary",
|
|
266
|
+
});
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
260
269
|
|
|
261
270
|
const content = await Bun.file(absolutePath).text();
|
|
262
271
|
const snapshotStore = options?.useHashLines ? options.snapshotStore : undefined;
|
package/src/utils/git.ts
CHANGED
|
@@ -74,8 +74,20 @@ export interface StatusOptions {
|
|
|
74
74
|
readonly z?: boolean;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
export interface CommitAuthor {
|
|
78
|
+
readonly date?: string;
|
|
79
|
+
readonly email: string;
|
|
80
|
+
readonly name: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface CommitDetails {
|
|
84
|
+
readonly author: CommitAuthor;
|
|
85
|
+
readonly message: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
77
88
|
export interface CommitOptions {
|
|
78
89
|
readonly allowEmpty?: boolean;
|
|
90
|
+
readonly author?: CommitAuthor;
|
|
79
91
|
readonly files?: readonly string[];
|
|
80
92
|
readonly signal?: AbortSignal;
|
|
81
93
|
}
|
|
@@ -91,6 +103,7 @@ export interface PatchOptions {
|
|
|
91
103
|
readonly cached?: boolean;
|
|
92
104
|
readonly check?: boolean;
|
|
93
105
|
readonly env?: Record<string, string | undefined>;
|
|
106
|
+
readonly threeWay?: boolean;
|
|
94
107
|
readonly signal?: AbortSignal;
|
|
95
108
|
}
|
|
96
109
|
|
|
@@ -359,6 +372,7 @@ function buildApplyArgs(patchPath: string, options: PatchOptions): string[] {
|
|
|
359
372
|
const args = ["apply"];
|
|
360
373
|
if (options.check) args.push("--check");
|
|
361
374
|
if (options.cached) args.push("--cached");
|
|
375
|
+
if (options.threeWay) args.push("--3way");
|
|
362
376
|
args.push("--binary", patchPath);
|
|
363
377
|
return args;
|
|
364
378
|
}
|
|
@@ -1122,6 +1136,10 @@ export const stage = {
|
|
|
1122
1136
|
/** Create a commit with the given message (passed via stdin). */
|
|
1123
1137
|
export async function commit(cwd: string, message: string, options: CommitOptions = {}): Promise<GitCommandResult> {
|
|
1124
1138
|
const args = ["commit", "-F", "-"];
|
|
1139
|
+
if (options.author) {
|
|
1140
|
+
args.push(`--author=${options.author.name} <${options.author.email}>`);
|
|
1141
|
+
if (options.author.date) args.push(`--date=${options.author.date}`);
|
|
1142
|
+
}
|
|
1125
1143
|
if (options.allowEmpty) args.push("--allow-empty");
|
|
1126
1144
|
if (options.files?.length) args.push("--", ...options.files);
|
|
1127
1145
|
return runChecked(cwd, args, { signal: options.signal, stdin: message });
|
|
@@ -1195,6 +1213,19 @@ export const show = Object.assign(
|
|
|
1195
1213
|
},
|
|
1196
1214
|
);
|
|
1197
1215
|
|
|
1216
|
+
/** Read commit message and author metadata for replay/rewrite flows. */
|
|
1217
|
+
export async function commitDetails(cwd: string, revision: string, signal?: AbortSignal): Promise<CommitDetails> {
|
|
1218
|
+
const raw = await runText(cwd, ["show", "-s", "--format=%an%x00%ae%x00%aI%x00%B", revision], {
|
|
1219
|
+
readOnly: true,
|
|
1220
|
+
signal,
|
|
1221
|
+
});
|
|
1222
|
+
const [name = "", email = "", date = "", ...messageParts] = raw.split("\0");
|
|
1223
|
+
return {
|
|
1224
|
+
author: { date, email, name },
|
|
1225
|
+
message: messageParts.join("\0").replace(/\n$/, ""),
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1198
1229
|
// ════════════════════════════════════════════════════════════════════════════
|
|
1199
1230
|
// API: log
|
|
1200
1231
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -1212,6 +1243,13 @@ export const log = {
|
|
|
1212
1243
|
},
|
|
1213
1244
|
};
|
|
1214
1245
|
|
|
1246
|
+
export const revList = {
|
|
1247
|
+
/** Commits in `base..head`, oldest first. */
|
|
1248
|
+
async range(cwd: string, base: string, head: string, signal?: AbortSignal): Promise<string[]> {
|
|
1249
|
+
return splitLines(await runText(cwd, ["rev-list", "--reverse", `${base}..${head}`], { readOnly: true, signal }));
|
|
1250
|
+
},
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1215
1253
|
// ════════════════════════════════════════════════════════════════════════════
|
|
1216
1254
|
// API: branch
|
|
1217
1255
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -35,7 +35,7 @@ const RECENCY_TO_DDG_DF: Record<NonNullable<SearchParams["recency"]>, string> =
|
|
|
35
35
|
* the orchestrator can fall through to the next provider with context.
|
|
36
36
|
*/
|
|
37
37
|
const BROWSER_USER_AGENT =
|
|
38
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
38
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
|
|
39
39
|
|
|
40
40
|
interface ParsedResult {
|
|
41
41
|
title: string;
|
|
@@ -130,15 +130,29 @@ async function callDuckDuckGoHtml(params: SearchParams): Promise<string> {
|
|
|
130
130
|
const form = new URLSearchParams({ q: params.query, kl: "us-en" });
|
|
131
131
|
const df = params.recency ? RECENCY_TO_DDG_DF[params.recency] : undefined;
|
|
132
132
|
if (df) form.set("df", df);
|
|
133
|
+
// Add b: "" parameter as specified in the browser fetch template to match real browser form submission
|
|
134
|
+
form.set("b", "");
|
|
133
135
|
|
|
134
136
|
const response = await (params.fetch ?? fetch)(DUCKDUCKGO_HTML_URL, {
|
|
135
137
|
method: "POST",
|
|
136
138
|
body: form.toString(),
|
|
137
139
|
headers: {
|
|
140
|
+
Accept:
|
|
141
|
+
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
142
|
+
"Accept-Language": "en,en-US;q=0.9",
|
|
143
|
+
"Cache-Control": "max-age=0",
|
|
138
144
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
145
|
+
Priority: "u=0, i",
|
|
146
|
+
"Sec-Ch-Ua": '"Google Chrome";v="149", "Chromium";v="149", "Not)A;Brand";v="24"',
|
|
147
|
+
"Sec-Ch-Ua-Mobile": "?0",
|
|
148
|
+
"Sec-Ch-Ua-Platform": '"macOS"',
|
|
149
|
+
"Sec-Fetch-Dest": "document",
|
|
150
|
+
"Sec-Fetch-Mode": "navigate",
|
|
151
|
+
"Sec-Fetch-Site": "same-origin",
|
|
152
|
+
"Sec-Fetch-User": "?1",
|
|
153
|
+
"Upgrade-Insecure-Requests": "1",
|
|
139
154
|
"User-Agent": BROWSER_USER_AGENT,
|
|
140
|
-
|
|
141
|
-
"Accept-Language": "en-US,en;q=0.5",
|
|
155
|
+
Referer: "https://html.duckduckgo.com/",
|
|
142
156
|
},
|
|
143
157
|
signal: withHardTimeout(params.signal),
|
|
144
158
|
});
|