@oh-my-pi/pi-coding-agent 17.3.7 → 17.3.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 +60 -0
- package/dist/{CHANGELOG-1hmwwt45.md → CHANGELOG-vr9cckb4.md} +60 -0
- package/dist/cli.js +2993 -3001
- package/dist/docs-index.generated.txt +1 -1
- package/dist/{tool-views.generated-jdfmzwmn.js → tool-views.generated-dd2km5r2.js} +19 -19
- package/dist/types/advisor/advise-tool.d.ts +4 -2
- package/dist/types/cli/auth-broker-cli.d.ts +15 -0
- package/dist/types/cli/update-cli.d.ts +8 -0
- package/dist/types/cli-commands.d.ts +10 -2
- package/dist/types/config/settings-schema.d.ts +28 -0
- package/dist/types/config/settings.d.ts +9 -0
- package/dist/types/extensibility/extensions/runner.d.ts +2 -1
- package/dist/types/launch/presence.d.ts +4 -1
- package/dist/types/mcp/oauth-credentials.d.ts +23 -0
- package/dist/types/mcp/oauth-flow.d.ts +11 -0
- package/dist/types/mnemopi/backend.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +12 -0
- package/dist/types/modes/controllers/input-controller.d.ts +2 -0
- package/dist/types/modes/interactive-mode.d.ts +25 -3
- package/dist/types/modes/types.d.ts +10 -0
- package/dist/types/session/agent-session.d.ts +3 -0
- package/dist/types/session/prewalk.d.ts +4 -0
- package/dist/types/session/session-entries.d.ts +0 -1
- package/dist/types/session/session-manager.d.ts +12 -0
- package/dist/types/session/session-stats.d.ts +13 -1
- package/dist/types/session/skill-title-input.d.ts +13 -0
- package/dist/types/subprocess/worker-client.d.ts +7 -4
- package/dist/types/task/label.d.ts +2 -0
- package/dist/types/task/render.d.ts +2 -0
- package/dist/types/tiny/completion-prompt.d.ts +2 -0
- package/dist/types/tiny/title-client.d.ts +6 -4
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- package/dist/types/tiny/worker.d.ts +27 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/read-format.d.ts +6 -0
- package/dist/types/tools/read-summary.d.ts +7 -1
- package/dist/types/utils/block-context.d.ts +14 -0
- package/dist/types/utils/fetch-timeout.d.ts +15 -0
- package/dist/types/utils/git.d.ts +25 -1
- package/dist/types/web/search/providers/tinyfish.d.ts +4 -0
- package/package.json +13 -13
- package/src/advisor/advise-tool.ts +5 -3
- package/src/cli/auth-broker-cli.ts +36 -1
- package/src/cli/profile-bootstrap.ts +2 -6
- package/src/cli/update-cli.ts +63 -11
- package/src/cli-commands.ts +61 -7
- package/src/commands/completions.ts +2 -1
- package/src/commit/agentic/index.ts +15 -2
- package/src/commit/git/diff.ts +6 -2
- package/src/config/model-resolver.ts +52 -6
- package/src/config/models-config.ts +2 -2
- package/src/config/settings-schema.ts +33 -0
- package/src/config/settings.ts +159 -30
- package/src/discovery/helpers.ts +45 -2
- package/src/discovery/omp-plugins.ts +2 -1
- package/src/discovery/opencode.ts +56 -3
- package/src/eval/js/process-entry.ts +4 -4
- package/src/export/html/tool-views.generated.js +19 -19
- package/src/extensibility/extensions/runner.ts +3 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +47 -0
- package/src/launch/client.ts +9 -4
- package/src/launch/presence.ts +19 -4
- package/src/lsp/defaults.json +1 -1
- package/src/mcp/manager.ts +41 -20
- package/src/mcp/oauth-credentials.ts +38 -0
- package/src/mcp/oauth-flow.ts +21 -0
- package/src/mcp/tool-bridge.ts +32 -16
- package/src/mnemopi/backend.ts +35 -3
- package/src/modes/components/model-hub.ts +37 -4
- package/src/modes/components/settings-selector.ts +17 -11
- package/src/modes/components/tool-execution.ts +97 -29
- package/src/modes/components/tree-selector.ts +7 -2
- package/src/modes/controllers/event-controller.ts +12 -2
- package/src/modes/controllers/input-controller.ts +64 -27
- package/src/modes/controllers/mcp-command-controller.ts +13 -4
- package/src/modes/interactive-mode.ts +79 -11
- package/src/modes/types.ts +11 -0
- package/src/prompts/system/memory-extraction-system.md +5 -22
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/session/agent-session.ts +52 -6
- package/src/session/messages.ts +6 -0
- package/src/session/prewalk.ts +25 -7
- package/src/session/session-entries.ts +0 -1
- package/src/session/session-maintenance.ts +10 -1
- package/src/session/session-manager.ts +15 -0
- package/src/session/session-stats.ts +24 -3
- package/src/session/settings-stream-fn.ts +7 -0
- package/src/session/skill-title-input.ts +32 -0
- package/src/session/turn-recovery.ts +23 -18
- package/src/subprocess/worker-client.ts +8 -5
- package/src/task/executor.ts +11 -0
- package/src/task/index.ts +2 -0
- package/src/task/label.ts +14 -1
- package/src/task/persisted-revive.ts +13 -0
- package/src/task/render.ts +1 -1
- package/src/task/structured-subagent.ts +5 -2
- package/src/tiny/completion-prompt.ts +16 -0
- package/src/tiny/title-client.ts +15 -6
- package/src/tiny/title-protocol.ts +8 -1
- package/src/tiny/worker.ts +21 -19
- package/src/tools/bash.ts +7 -1
- package/src/tools/read-format.ts +16 -2
- package/src/tools/read-summary.ts +9 -4
- package/src/tools/read.ts +306 -72
- package/src/utils/block-context.ts +15 -1
- package/src/utils/fetch-timeout.ts +33 -0
- package/src/utils/git.ts +54 -11
- package/src/web/search/providers/browser-page.ts +21 -3
- package/src/web/search/providers/tinyfish.ts +26 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TextGenerationPipeline } from "@huggingface/transformers";
|
|
2
|
+
|
|
3
|
+
export function buildCompletionPrompt(
|
|
4
|
+
tokenizer: TextGenerationPipeline["tokenizer"],
|
|
5
|
+
promptText: string,
|
|
6
|
+
systemPrompt?: string,
|
|
7
|
+
): string {
|
|
8
|
+
const userMessage = { role: "user", content: promptText };
|
|
9
|
+
const chat = systemPrompt?.trim() ? [{ role: "system", content: systemPrompt.trim() }, userMessage] : [userMessage];
|
|
10
|
+
const chatTemplateOptions = {
|
|
11
|
+
add_generation_prompt: true,
|
|
12
|
+
tokenize: false,
|
|
13
|
+
enable_thinking: false,
|
|
14
|
+
};
|
|
15
|
+
return `${tokenizer.apply_chat_template(chat, chatTemplateOptions)}`;
|
|
16
|
+
}
|
package/src/tiny/title-client.ts
CHANGED
|
@@ -52,6 +52,12 @@ export interface TinyTitleGenerateOptions {
|
|
|
52
52
|
systemPrompt?: string;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
export interface TinyModelCompletionOptions {
|
|
56
|
+
maxTokens?: number;
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
systemPrompt?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
function normalizeTinyTitleGenerateOptions(
|
|
56
62
|
options: AbortSignal | TinyTitleGenerateOptions | undefined,
|
|
57
63
|
): TinyTitleGenerateOptions {
|
|
@@ -260,11 +266,7 @@ export class TinyTitleClient {
|
|
|
260
266
|
}
|
|
261
267
|
}
|
|
262
268
|
|
|
263
|
-
async complete(
|
|
264
|
-
modelKey: string,
|
|
265
|
-
prompt: string,
|
|
266
|
-
options: { maxTokens?: number; signal?: AbortSignal } = {},
|
|
267
|
-
): Promise<string | null> {
|
|
269
|
+
async complete(modelKey: string, prompt: string, options: TinyModelCompletionOptions = {}): Promise<string | null> {
|
|
268
270
|
if (!isTinyMemoryLocalModelKey(modelKey)) return null;
|
|
269
271
|
if (options.signal?.aborted || this.#failedModels.has(modelKey)) return null;
|
|
270
272
|
|
|
@@ -281,7 +283,14 @@ export class TinyTitleClient {
|
|
|
281
283
|
};
|
|
282
284
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
283
285
|
try {
|
|
284
|
-
worker.send({
|
|
286
|
+
worker.send({
|
|
287
|
+
type: "complete",
|
|
288
|
+
id,
|
|
289
|
+
modelKey,
|
|
290
|
+
prompt,
|
|
291
|
+
maxTokens: options.maxTokens,
|
|
292
|
+
systemPrompt: options.systemPrompt,
|
|
293
|
+
});
|
|
285
294
|
return await promise;
|
|
286
295
|
} finally {
|
|
287
296
|
options.signal?.removeEventListener("abort", abort);
|
|
@@ -30,7 +30,14 @@ export interface TinyTitleProgressEvent {
|
|
|
30
30
|
export type TinyTitleWorkerInbound =
|
|
31
31
|
| { type: "ping"; id: string }
|
|
32
32
|
| { type: "generate"; id: string; modelKey: TinyTitleLocalModelKey; message: string; systemPrompt?: string }
|
|
33
|
-
| {
|
|
33
|
+
| {
|
|
34
|
+
type: "complete";
|
|
35
|
+
id: string;
|
|
36
|
+
modelKey: TinyLocalModelKey;
|
|
37
|
+
prompt: string;
|
|
38
|
+
maxTokens?: number;
|
|
39
|
+
systemPrompt?: string;
|
|
40
|
+
}
|
|
34
41
|
| { type: "download"; id: string; modelKey: TinyLocalModelKey };
|
|
35
42
|
|
|
36
43
|
export type TinyTitleWorkerOutbound =
|
package/src/tiny/worker.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
sendProgress,
|
|
20
20
|
type TransformersRuntimeMetadata,
|
|
21
21
|
} from "../subprocess/worker-runtime";
|
|
22
|
+
import { buildCompletionPrompt } from "./completion-prompt";
|
|
22
23
|
import { resolveTinyModelDevicePreference, type TinyModelDevice, tinyModelDeviceLoadOrder } from "./device";
|
|
23
24
|
import { resolveTinyModelDtypeOverride, type TinyModelDtype } from "./dtype";
|
|
24
25
|
import { formatTitleUserMessage } from "./message-preproc";
|
|
@@ -42,7 +43,7 @@ const TINY_TITLE_SYSTEM_PROMPT = prompt.render(titleSystemPrompt);
|
|
|
42
43
|
const tinyModelDevicePreference = resolveTinyModelDevicePreference();
|
|
43
44
|
const tinyModelDtypeOverride = resolveTinyModelDtypeOverride();
|
|
44
45
|
|
|
45
|
-
interface TransformersRuntime extends TransformersRuntimeMetadata {
|
|
46
|
+
export interface TransformersRuntime extends TransformersRuntimeMetadata {
|
|
46
47
|
env: {
|
|
47
48
|
cacheDir?: string;
|
|
48
49
|
allowLocalModels?: boolean;
|
|
@@ -79,7 +80,13 @@ function getTinyTitleRuntimeDir(): string {
|
|
|
79
80
|
);
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
/** Stops generation at the first occurrence of `text` in the *generated* tokens.
|
|
84
|
+
*
|
|
85
|
+
* The window must be anchored to the generation boundary, not to the end of the
|
|
86
|
+
* whole sequence: a prompt that itself contains the stop string (chat-level
|
|
87
|
+
* few-shot examples ending in `</title>`, for instance) would otherwise match on
|
|
88
|
+
* prompt tokens and stop before the model emits anything. */
|
|
89
|
+
export function createStopOnTextCriteria(
|
|
83
90
|
transformers: TransformersRuntime,
|
|
84
91
|
tokenizer: TextGenerationPipeline["tokenizer"],
|
|
85
92
|
text: string,
|
|
@@ -87,6 +94,8 @@ function createStopOnTextCriteria(
|
|
|
87
94
|
class StopOnTextCriteria extends transformers.StoppingCriteria {
|
|
88
95
|
#tokenizer: TextGenerationPipeline["tokenizer"];
|
|
89
96
|
#text: string;
|
|
97
|
+
/** First generated index per batch entry, captured on the first call. */
|
|
98
|
+
#generatedStarts: number[] = [];
|
|
90
99
|
|
|
91
100
|
constructor() {
|
|
92
101
|
super();
|
|
@@ -95,8 +104,10 @@ function createStopOnTextCriteria(
|
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
override _call(inputIds: number[][]): boolean[] {
|
|
98
|
-
return inputIds.map(ids => {
|
|
99
|
-
const
|
|
107
|
+
return inputIds.map((ids, index) => {
|
|
108
|
+
const generatedStart = this.#generatedStarts[index] ?? Math.max(0, ids.length - 1);
|
|
109
|
+
this.#generatedStarts[index] = generatedStart;
|
|
110
|
+
const tail = ids.slice(Math.max(generatedStart, ids.length - STOP_DECODE_WINDOW_TOKENS));
|
|
100
111
|
const decoded = this.#tokenizer.decode(tail, {
|
|
101
112
|
skip_special_tokens: false,
|
|
102
113
|
clean_up_tokenization_spaces: false,
|
|
@@ -265,21 +276,10 @@ async function generateTitle(
|
|
|
265
276
|
return extractTinyTitle(output[0]?.generated_text ?? "", message);
|
|
266
277
|
}
|
|
267
278
|
|
|
268
|
-
function buildCompletionPrompt(generator: TextGenerationPipeline, promptText: string): string {
|
|
269
|
-
const chat = [{ role: "user", content: promptText }];
|
|
270
|
-
const chatTemplateOptions = {
|
|
271
|
-
add_generation_prompt: true,
|
|
272
|
-
tokenize: false,
|
|
273
|
-
enable_thinking: false,
|
|
274
|
-
};
|
|
275
|
-
return `${generator.tokenizer.apply_chat_template(chat, chatTemplateOptions)}`;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
279
|
/**
|
|
279
|
-
*
|
|
280
|
-
* and
|
|
281
|
-
*
|
|
282
|
-
* caller's own parser. Output is capped to keep local inference latency bounded.
|
|
280
|
+
* Completion path for Mnemopi memory tasks. Extraction can carry a dedicated
|
|
281
|
+
* system prompt and user payload; consolidation retains the generic user-only
|
|
282
|
+
* prompt. Output is capped to keep local inference latency bounded.
|
|
283
283
|
*/
|
|
284
284
|
async function generateCompletion(
|
|
285
285
|
transport: TinyTitleTransport,
|
|
@@ -287,9 +287,10 @@ async function generateCompletion(
|
|
|
287
287
|
modelKey: TinyLocalModelKey,
|
|
288
288
|
promptText: string,
|
|
289
289
|
maxTokens: number | undefined,
|
|
290
|
+
systemPrompt: string | undefined,
|
|
290
291
|
): Promise<string | null> {
|
|
291
292
|
const generator = await loadPipeline(modelKey, transport, requestId);
|
|
292
|
-
const text = buildCompletionPrompt(generator, promptText);
|
|
293
|
+
const text = buildCompletionPrompt(generator.tokenizer, promptText, systemPrompt);
|
|
293
294
|
const requested = maxTokens ?? MEMORY_COMPLETION_DEFAULT_MAX_NEW_TOKENS;
|
|
294
295
|
const maxNewTokens = Math.min(Math.max(1, requested), COMPLETION_MAX_NEW_TOKENS);
|
|
295
296
|
const output = (await generator(text, {
|
|
@@ -332,6 +333,7 @@ async function handleQueuedRequest(
|
|
|
332
333
|
request.modelKey,
|
|
333
334
|
request.prompt,
|
|
334
335
|
request.maxTokens,
|
|
336
|
+
request.systemPrompt,
|
|
335
337
|
);
|
|
336
338
|
transport.send({ type: "completion", id: request.id, text });
|
|
337
339
|
return;
|
package/src/tools/bash.ts
CHANGED
|
@@ -166,7 +166,13 @@ function shellBuiltinsDisabled(settings: Settings): boolean {
|
|
|
166
166
|
*/
|
|
167
167
|
export const CRITICAL_BASH_PATTERNS = [
|
|
168
168
|
// Recursive destruction.
|
|
169
|
-
|
|
169
|
+
// Options may sit on either side of the recursive/force flag, so only that flag is pinned and
|
|
170
|
+
// any other options are skipped: `rm -rf /`, `rm -rf -- /`, `rm --recursive --force /`,
|
|
171
|
+
// `rm -rf -v /`, `rm -v -rf /`. An absolute target is still required.
|
|
172
|
+
/\brm\s+(?:-\S+\s+)*(?:-[a-z]*[rRfF][a-z]*|--recursive|--force)\s+(?:-\S+\s+)*\//i,
|
|
173
|
+
// `--no-preserve-root` defeats coreutils' own refusal to recurse on `/`, so it is critical
|
|
174
|
+
// wherever it appears — including forms this list would otherwise reach only via the target.
|
|
175
|
+
/\brm\s+(?:-\S+\s+)*--no-preserve-root\b/i,
|
|
170
176
|
/\bsudo\s+rm\b/i, // any `sudo rm`.
|
|
171
177
|
/\bchmod\s+-R\s+[0-7]+\s+\//i, // `chmod -R 777 /`.
|
|
172
178
|
/\bchmod\s+-R\s+[ugoa+\-=rwxXst,]+\s+\//, // `chmod -R u+x /`, `chmod -R u+rwx,o+w /etc` (symbolic mode, root target).
|
package/src/tools/read-format.ts
CHANGED
|
@@ -72,7 +72,20 @@ export async function readHashlineHeaderContext(
|
|
|
72
72
|
absolutePath: string,
|
|
73
73
|
cwd: string,
|
|
74
74
|
): Promise<HashlineHeaderContext> {
|
|
75
|
-
|
|
75
|
+
return hashlineHeaderContextForText(session, absolutePath, cwd, await Bun.file(absolutePath).text());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* {@link readHashlineHeaderContext} for a caller that already holds the file's
|
|
80
|
+
* full text, so the file is not reopened just to hash it. Line endings are
|
|
81
|
+
* normalized here, exactly as the reading variant does.
|
|
82
|
+
*/
|
|
83
|
+
export function hashlineHeaderContextForText(
|
|
84
|
+
session: ToolSession,
|
|
85
|
+
absolutePath: string,
|
|
86
|
+
cwd: string,
|
|
87
|
+
fullText: string,
|
|
88
|
+
): HashlineHeaderContext {
|
|
76
89
|
const context = recordFullHashlineContext(
|
|
77
90
|
session,
|
|
78
91
|
absolutePath,
|
|
@@ -392,6 +405,7 @@ export function buildInMemoryTextResult(
|
|
|
392
405
|
const buildLineEntries = (endLineDisplay: number): LineEntry[] =>
|
|
393
406
|
buildLineEntriesWithBlockContext(allLines, [{ startLine: startLineDisplay, endLine: endLineDisplay }], {
|
|
394
407
|
path: options.sourcePath,
|
|
408
|
+
text,
|
|
395
409
|
});
|
|
396
410
|
|
|
397
411
|
let outputText: string;
|
|
@@ -534,7 +548,7 @@ export function buildInMemoryMultiRangeResult(
|
|
|
534
548
|
if (options.raw === true) {
|
|
535
549
|
outputText = rawParts.length > 0 ? rawParts.join("\n\n…\n\n") : "";
|
|
536
550
|
} else if (visibleSpans.length > 0) {
|
|
537
|
-
const entries = buildLineEntriesWithBlockContext(allLines, visibleSpans, { path: options.sourcePath });
|
|
551
|
+
const entries = buildLineEntriesWithBlockContext(allLines, visibleSpans, { path: options.sourcePath, text });
|
|
538
552
|
if (shouldAddHashLines) seenLines = lineNumbersFromEntries(entries);
|
|
539
553
|
const firstLine = entries.find(entry => entry.kind === "line");
|
|
540
554
|
if (firstLine?.kind === "line") {
|
|
@@ -52,21 +52,26 @@ export function routeReadThroughBridge(
|
|
|
52
52
|
if (!bridge?.capabilities.readTextFile || !bridge.readTextFile) return undefined;
|
|
53
53
|
return bridge.readTextFile({ path: absolutePath, ...options });
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Structural summary of `absolutePath`, or `null` when the file is too large,
|
|
57
|
+
* too short, or unparseable. `diskText` lets a caller that already read the file
|
|
58
|
+
* hand those bytes over instead of forcing a second read; an ACP bridge still
|
|
59
|
+
* wins, since the editor's buffer is the source of truth.
|
|
60
|
+
*/
|
|
55
61
|
export async function trySummarize(
|
|
56
62
|
session: ToolSession,
|
|
57
63
|
absolutePath: string,
|
|
58
64
|
fileSize: number,
|
|
59
65
|
signal?: AbortSignal,
|
|
66
|
+
diskText?: string,
|
|
60
67
|
): Promise<SummaryResult | null> {
|
|
61
68
|
if (fileSize > MAX_SUMMARY_BYTES) return null;
|
|
62
69
|
|
|
63
70
|
try {
|
|
64
71
|
throwIfAborted(signal);
|
|
65
72
|
const bridgePromise = routeReadThroughBridge(session, absolutePath);
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
? await bridgePromise.catch(() => Bun.file(absolutePath).text())
|
|
69
|
-
: await Bun.file(absolutePath).text();
|
|
73
|
+
const readDisk = async () => diskText ?? (await Bun.file(absolutePath).text());
|
|
74
|
+
const code = bridgePromise !== undefined ? await bridgePromise.catch(readDisk) : await readDisk();
|
|
70
75
|
throwIfAborted(signal);
|
|
71
76
|
const lineCount = countTextLines(code);
|
|
72
77
|
if (lineCount > MAX_SUMMARY_LINES) return null;
|