@oh-my-pi/pi-coding-agent 15.6.0 → 15.7.1
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 +35 -0
- package/dist/types/capability/rule-buckets.d.ts +30 -0
- package/dist/types/capability/rule.d.ts +7 -0
- package/dist/types/cli/completion-gen.d.ts +80 -0
- package/dist/types/commands/complete.d.ts +6 -0
- package/dist/types/commands/completions.d.ts +13 -0
- package/dist/types/commands/setup.d.ts +10 -1
- package/dist/types/config/settings-schema.d.ts +170 -10
- package/dist/types/discovery/builtin-defaults.d.ts +1 -0
- package/dist/types/discovery/builtin-rules/index.d.ts +7 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/edit/hashline/block-resolver.d.ts +9 -0
- package/dist/types/edit/hashline/index.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +3 -0
- package/dist/types/eval/py/runtime.d.ts +11 -1
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/segment-track.d.ts +22 -0
- package/dist/types/modes/components/welcome.d.ts +21 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -2
- package/dist/types/modes/setup-wizard/index.d.ts +16 -0
- package/dist/types/modes/setup-wizard/scenes/glyph.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/outro.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/providers.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +19 -0
- package/dist/types/modes/setup-wizard/scenes/splash.d.ts +11 -0
- package/dist/types/modes/setup-wizard/scenes/theme.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +43 -0
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +19 -0
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +14 -0
- package/dist/types/modes/theme/shimmer.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +11 -0
- package/dist/types/modes/types.d.ts +5 -1
- package/dist/types/tiny/device.d.ts +78 -0
- package/dist/types/tiny/dtype.d.ts +85 -0
- package/dist/types/tiny/models.d.ts +6 -6
- package/dist/types/tiny/text.d.ts +15 -0
- package/dist/types/tiny/title-client.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +0 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/index.d.ts +0 -1
- package/dist/types/tui/code-cell.d.ts +2 -0
- package/dist/types/tui/output-block.d.ts +17 -0
- package/package.json +9 -9
- package/src/capability/rule-buckets.ts +64 -0
- package/src/capability/rule.ts +8 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/setup-cli.ts +5 -3
- package/src/cli-commands.ts +2 -0
- package/src/cli.ts +1 -7
- package/src/commands/complete.ts +66 -0
- package/src/commands/completions.ts +60 -0
- package/src/commands/setup.ts +29 -4
- package/src/config/settings-schema.ts +70 -11
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/index.ts +48 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
- package/src/discovery/builtin-rules/rs-result-type.md +19 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
- package/src/discovery/builtin-rules/ts-import-type.md +42 -0
- package/src/discovery/builtin-rules/ts-no-any.md +56 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
- package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +50 -0
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
- package/src/discovery/builtin-rules/ts-set-map.md +28 -0
- package/src/discovery/index.ts +1 -0
- package/src/edit/hashline/block-resolver.ts +14 -0
- package/src/edit/hashline/diff.ts +4 -1
- package/src/edit/hashline/execute.ts +2 -1
- package/src/edit/hashline/index.ts +1 -0
- package/src/eval/py/kernel.ts +37 -15
- package/src/eval/py/runtime.ts +57 -28
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -12
- package/src/export/ttsr.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +7 -8
- package/src/main.ts +18 -1
- package/src/modes/components/hook-selector.ts +15 -17
- package/src/modes/components/index.ts +1 -0
- package/src/modes/components/segment-track.ts +52 -0
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +5 -1
- package/src/modes/components/welcome.ts +47 -42
- package/src/modes/controllers/input-controller.ts +12 -21
- package/src/modes/interactive-mode.ts +17 -5
- package/src/modes/setup-wizard/index.ts +88 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +69 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +193 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +299 -0
- package/src/modes/setup-wizard/scenes/types.ts +48 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +128 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
- package/src/modes/theme/shimmer.ts +5 -0
- package/src/modes/theme/theme.ts +44 -20
- package/src/modes/types.ts +6 -1
- package/src/prompts/system/orchestrate-notice.md +1 -1
- package/src/prompts/tools/read.md +4 -0
- package/src/sdk.ts +5 -15
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/tiny/device.ts +117 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/models.ts +7 -6
- package/src/tiny/text.ts +36 -1
- package/src/tiny/title-client.ts +58 -3
- package/src/tiny/worker.ts +93 -29
- package/src/tools/bash.ts +16 -13
- package/src/tools/eval.ts +9 -4
- package/src/tools/index.ts +0 -11
- package/src/tools/read.ts +1 -0
- package/src/tools/renderers.ts +0 -2
- package/src/tui/code-cell.ts +6 -1
- package/src/tui/output-block.ts +199 -38
- package/dist/types/tools/recipe/index.d.ts +0 -46
- package/dist/types/tools/recipe/render.d.ts +0 -36
- package/dist/types/tools/recipe/runner.d.ts +0 -60
- package/dist/types/tools/recipe/runners/cargo.d.ts +0 -16
- package/dist/types/tools/recipe/runners/index.d.ts +0 -2
- package/dist/types/tools/recipe/runners/just.d.ts +0 -2
- package/dist/types/tools/recipe/runners/make.d.ts +0 -2
- package/dist/types/tools/recipe/runners/pkg.d.ts +0 -2
- package/dist/types/tools/recipe/runners/task.d.ts +0 -2
- package/src/prompts/tools/recipe.md +0 -16
- package/src/tools/recipe/index.ts +0 -81
- package/src/tools/recipe/render.ts +0 -19
- package/src/tools/recipe/runner.ts +0 -219
- package/src/tools/recipe/runners/cargo.ts +0 -131
- package/src/tools/recipe/runners/index.ts +0 -8
- package/src/tools/recipe/runners/just.ts +0 -73
- package/src/tools/recipe/runners/make.ts +0 -101
- package/src/tools/recipe/runners/pkg.ts +0 -167
- package/src/tools/recipe/runners/task.ts +0 -72
package/src/tiny/models.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Default session-title model: the online pi/smol path (no local download /
|
|
1
|
+
/** Default session-title model: the online pi/smol path (no local download / on-device inference). */
|
|
2
2
|
export const ONLINE_TINY_TITLE_MODEL_KEY = "online";
|
|
3
3
|
/** Local model the `tiny-models` CLI downloads when none is named. Not the session-title default — that is {@link ONLINE_TINY_TITLE_MODEL_KEY}. */
|
|
4
4
|
export const DEFAULT_TINY_TITLE_LOCAL_MODEL_KEY = "lfm2-700m";
|
|
@@ -19,7 +19,7 @@ export const TINY_TITLE_LOCAL_MODELS = [
|
|
|
19
19
|
dtype: "q4",
|
|
20
20
|
label: "LFM2 350M",
|
|
21
21
|
description: "Recommended local model; best speed/quality balance, about 212 MB cached.",
|
|
22
|
-
contextNote: "Best local default from the
|
|
22
|
+
contextNote: "Best local default from the title-generation spike.",
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
key: "qwen3-0.6b",
|
|
@@ -83,7 +83,7 @@ export const TINY_TITLE_MODEL_OPTIONS = [
|
|
|
83
83
|
{
|
|
84
84
|
value: ONLINE_TINY_TITLE_MODEL_KEY,
|
|
85
85
|
label: "Online (pi/smol)",
|
|
86
|
-
description: "Current online title generation path; no local model download or
|
|
86
|
+
description: "Current online title generation path; no local model download or on-device inference.",
|
|
87
87
|
},
|
|
88
88
|
...TINY_TITLE_LOCAL_MODELS.map(model => ({
|
|
89
89
|
value: model.key,
|
|
@@ -110,7 +110,7 @@ export const DEFAULT_MEMORY_LOCAL_MODEL_KEY = "qwen3-1.7b";
|
|
|
110
110
|
/**
|
|
111
111
|
* Local models for Mnemosyne memory tasks (fact extraction + consolidation).
|
|
112
112
|
* These are larger (1B-1.7B) than the title models: structured extraction and
|
|
113
|
-
* faithful summarization need more capacity than 3-6 word titles. All q4
|
|
113
|
+
* faithful summarization need more capacity than 3-6 word titles. All q4.
|
|
114
114
|
* Ranking/recipe rationale lives in docs/local-models.md.
|
|
115
115
|
*/
|
|
116
116
|
export const TINY_MEMORY_LOCAL_MODELS = [
|
|
@@ -121,7 +121,7 @@ export const TINY_MEMORY_LOCAL_MODELS = [
|
|
|
121
121
|
label: "Qwen3 1.7B",
|
|
122
122
|
description:
|
|
123
123
|
"Recommended; most disciplined extraction (ignores chit-chat), good consolidation, about 1.1 GB cached.",
|
|
124
|
-
contextNote: "Best single-model pick for memory from the
|
|
124
|
+
contextNote: "Best single-model pick for memory from the local experiment.",
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
key: "gemma-3-1b",
|
|
@@ -176,7 +176,8 @@ export const TINY_MEMORY_MODEL_OPTIONS = [
|
|
|
176
176
|
{
|
|
177
177
|
value: ONLINE_MEMORY_MODEL_KEY,
|
|
178
178
|
label: "Online (smol/remote)",
|
|
179
|
-
description:
|
|
179
|
+
description:
|
|
180
|
+
"Use the configured Mnemosyne LLM mode (smol or remote); no local model download or on-device inference.",
|
|
180
181
|
},
|
|
181
182
|
...TINY_MEMORY_LOCAL_MODELS.map(model => ({
|
|
182
183
|
value: model.key,
|
package/src/tiny/text.ts
CHANGED
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
export const MAX_TITLE_INPUT_CHARS = 2000;
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Minimum length of code-stripped input below which we fall back to the
|
|
5
|
+
* original message. Guards against messages that are (almost) entirely a code
|
|
6
|
+
* block — stripping would otherwise leave the model nothing to title from.
|
|
7
|
+
*/
|
|
8
|
+
const MIN_STRIPPED_TITLE_CHARS = 12;
|
|
9
|
+
/** Matches a fenced code block (3+ backticks), including an unterminated trailing fence. */
|
|
10
|
+
const FENCED_CODE_BLOCK = /```+[\s\S]*?(?:```+|$)/g;
|
|
11
|
+
|
|
3
12
|
export function truncateTitleInput(message: string): string {
|
|
4
13
|
return message.length > MAX_TITLE_INPUT_CHARS ? `${message.slice(0, MAX_TITLE_INPUT_CHARS)}…` : message;
|
|
5
14
|
}
|
|
6
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Strip fenced code blocks from a message before titling.
|
|
18
|
+
*
|
|
19
|
+
* Small title models latch onto literal text inside code blocks — e.g. a pasted
|
|
20
|
+
* UI mockup containing "Welcome to Claude Code v2.1.158" yields that string as
|
|
21
|
+
* the title instead of the surrounding intent. Removing fenced blocks leaves the
|
|
22
|
+
* prose that actually describes the task. Inline code (single backticks) is kept
|
|
23
|
+
* — it is short, high-signal context like `/login`.
|
|
24
|
+
*
|
|
25
|
+
* Falls back to the original message when stripping leaves too little to title
|
|
26
|
+
* (a message that is essentially just a code block).
|
|
27
|
+
*/
|
|
28
|
+
export function stripCodeBlocks(message: string): string {
|
|
29
|
+
const cleaned = message
|
|
30
|
+
.replace(FENCED_CODE_BLOCK, " ")
|
|
31
|
+
.replace(/[ \t]+/g, " ")
|
|
32
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
33
|
+
.trim();
|
|
34
|
+
return cleaned.length >= MIN_STRIPPED_TITLE_CHARS ? cleaned : message;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Prepare a raw user message for titling: drop code blocks, then bound length. */
|
|
38
|
+
export function prepareTitleInput(message: string): string {
|
|
39
|
+
return truncateTitleInput(stripCodeBlocks(message));
|
|
40
|
+
}
|
|
41
|
+
|
|
7
42
|
export function formatTitleUserMessage(message: string): string {
|
|
8
|
-
return `<user-message>\n${
|
|
43
|
+
return `<user-message>\n${prepareTitleInput(message)}\n</user-message>`;
|
|
9
44
|
}
|
|
10
45
|
|
|
11
46
|
export function normalizeGeneratedTitle(value: string | null | undefined): string | null {
|
package/src/tiny/title-client.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { isCompiledBinary, logger } from "@oh-my-pi/pi-utils";
|
|
1
|
+
import { $env, isCompiledBinary, logger } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import { settings } from "../config/settings";
|
|
3
|
+
import { tinyModelDeviceSettingToEnv } from "./device";
|
|
4
|
+
import { tinyModelDtypeSettingToEnv } from "./dtype";
|
|
2
5
|
import {
|
|
3
6
|
isTinyLocalModelKey,
|
|
4
7
|
isTinyMemoryLocalModelKey,
|
|
@@ -28,10 +31,62 @@ export interface TinyTitleDownloadOptions {
|
|
|
28
31
|
|
|
29
32
|
const SMOKE_TEST_TIMEOUT_MS = 5_000;
|
|
30
33
|
|
|
34
|
+
function readTinyModelSetting(path: "providers.tinyModelDevice" | "providers.tinyModelDtype"): string | undefined {
|
|
35
|
+
try {
|
|
36
|
+
const value = settings.get(path);
|
|
37
|
+
return typeof value === "string" ? value : undefined;
|
|
38
|
+
} catch {
|
|
39
|
+
// Settings may be uninitialized (e.g. `omp --smoke-test`); fall back to env/default.
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Decide which `PI_TINY_DEVICE` / `PI_TINY_DTYPE` vars to overlay onto the worker
|
|
46
|
+
* env. A present env var wins (left untouched); otherwise the mapped persisted
|
|
47
|
+
* setting is used. Returns only the keys to add — never the default sentinel.
|
|
48
|
+
* Pure for testability; see {@link tinyWorkerEnv} for the spawn-time glue.
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export function tinyWorkerEnvOverlay(
|
|
52
|
+
env: Record<string, string | undefined>,
|
|
53
|
+
deviceSetting: string | undefined,
|
|
54
|
+
dtypeSetting: string | undefined,
|
|
55
|
+
): Record<string, string> {
|
|
56
|
+
const overlay: Record<string, string> = {};
|
|
57
|
+
if (!env.PI_TINY_DEVICE) {
|
|
58
|
+
const device = tinyModelDeviceSettingToEnv(deviceSetting);
|
|
59
|
+
if (device) overlay.PI_TINY_DEVICE = device;
|
|
60
|
+
}
|
|
61
|
+
if (!env.PI_TINY_DTYPE) {
|
|
62
|
+
const dtype = tinyModelDtypeSettingToEnv(dtypeSetting);
|
|
63
|
+
if (dtype) overlay.PI_TINY_DTYPE = dtype;
|
|
64
|
+
}
|
|
65
|
+
return overlay;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Env handed to the tiny-model worker. The `PI_TINY_DEVICE` / `PI_TINY_DTYPE` env
|
|
70
|
+
* vars win; otherwise the persisted `providers.tinyModelDevice` /
|
|
71
|
+
* `providers.tinyModelDtype` settings are mapped onto those vars so the worker's
|
|
72
|
+
* env-based resolution picks them up. Resolved once at spawn (pipelines are cached).
|
|
73
|
+
*/
|
|
74
|
+
function tinyWorkerEnv(): Record<string, string> | undefined {
|
|
75
|
+
const overlay = tinyWorkerEnvOverlay(
|
|
76
|
+
$env,
|
|
77
|
+
readTinyModelSetting("providers.tinyModelDevice"),
|
|
78
|
+
readTinyModelSetting("providers.tinyModelDtype"),
|
|
79
|
+
);
|
|
80
|
+
if (Object.keys(overlay).length === 0) return undefined;
|
|
81
|
+
return { ...($env as Record<string, string>), ...overlay };
|
|
82
|
+
}
|
|
83
|
+
|
|
31
84
|
export function createTinyTitleWorker(): Worker {
|
|
85
|
+
const env = tinyWorkerEnv();
|
|
86
|
+
const options: WorkerOptions = env ? { type: "module", env } : { type: "module" };
|
|
32
87
|
return isCompiledBinary()
|
|
33
|
-
? new Worker("./packages/coding-agent/src/tiny/worker.ts",
|
|
34
|
-
: new Worker(new URL("./worker.ts", import.meta.url).href,
|
|
88
|
+
? new Worker("./packages/coding-agent/src/tiny/worker.ts", options)
|
|
89
|
+
: new Worker(new URL("./worker.ts", import.meta.url).href, options);
|
|
35
90
|
}
|
|
36
91
|
|
|
37
92
|
function wrapBunWorker(worker: Worker): WorkerHandle {
|
package/src/tiny/worker.ts
CHANGED
|
@@ -11,7 +11,14 @@ import type {
|
|
|
11
11
|
import { getTinyModelsCacheDir, isCompiledBinary, prompt } from "@oh-my-pi/pi-utils";
|
|
12
12
|
import packageJson from "../../package.json" with { type: "json" };
|
|
13
13
|
import tinyTitleSystemPrompt from "../prompts/system/tiny-title-system.md" with { type: "text" };
|
|
14
|
-
import {
|
|
14
|
+
import { resolveTinyModelDevicePreference, type TinyModelDevice, tinyModelDeviceLoadOrder } from "./device";
|
|
15
|
+
import { resolveTinyModelDtypeOverride, type TinyModelDtype } from "./dtype";
|
|
16
|
+
import {
|
|
17
|
+
getTinyLocalModelSpec,
|
|
18
|
+
type TinyLocalModelKey,
|
|
19
|
+
type TinyTitleLocalModelKey,
|
|
20
|
+
type TinyTitleLocalModelSpec,
|
|
21
|
+
} from "./models";
|
|
15
22
|
import { formatTitleUserMessage, normalizeGeneratedTitle } from "./text";
|
|
16
23
|
import type {
|
|
17
24
|
TinyTitleProgressEvent,
|
|
@@ -31,6 +38,9 @@ const sourceRequire = createRequire(import.meta.url);
|
|
|
31
38
|
const INSTALL_LOCK_ATTEMPTS = 240;
|
|
32
39
|
const INSTALL_LOCK_SLEEP_MS = 250;
|
|
33
40
|
|
|
41
|
+
const tinyModelDevicePreference = resolveTinyModelDevicePreference();
|
|
42
|
+
const tinyModelDtypeOverride = resolveTinyModelDtypeOverride();
|
|
43
|
+
|
|
34
44
|
interface TransformersRuntime {
|
|
35
45
|
env: {
|
|
36
46
|
cacheDir?: string;
|
|
@@ -45,8 +55,8 @@ interface TransformersRuntime {
|
|
|
45
55
|
task: "text-generation",
|
|
46
56
|
model: string,
|
|
47
57
|
options: {
|
|
48
|
-
device:
|
|
49
|
-
dtype:
|
|
58
|
+
device: TinyModelDevice;
|
|
59
|
+
dtype: TinyModelDtype;
|
|
50
60
|
progress_callback: (info: ProgressInfo) => void;
|
|
51
61
|
},
|
|
52
62
|
) => Promise<TextGenerationPipeline>;
|
|
@@ -304,6 +314,63 @@ function sendProgress(
|
|
|
304
314
|
transport.send({ type: "progress", id, event: toProgressEvent(modelKey, info) });
|
|
305
315
|
}
|
|
306
316
|
|
|
317
|
+
function errorMessage(error: unknown): string {
|
|
318
|
+
return error instanceof Error ? error.message : String(error);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function loadPipelineOnDevice(
|
|
322
|
+
transformers: TransformersRuntime,
|
|
323
|
+
spec: TinyTitleLocalModelSpec,
|
|
324
|
+
modelKey: TinyLocalModelKey,
|
|
325
|
+
transport: TinyTitleTransport,
|
|
326
|
+
requestId: string,
|
|
327
|
+
device: TinyModelDevice,
|
|
328
|
+
): Promise<TextGenerationPipeline> {
|
|
329
|
+
return transformers.pipeline("text-generation", spec.repo, {
|
|
330
|
+
device,
|
|
331
|
+
dtype: tinyModelDtypeOverride ?? spec.dtype,
|
|
332
|
+
progress_callback: info => sendProgress(transport, requestId, modelKey, info),
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function loadPipelineWithDeviceFallback(
|
|
337
|
+
transformers: TransformersRuntime,
|
|
338
|
+
spec: TinyTitleLocalModelSpec,
|
|
339
|
+
modelKey: TinyLocalModelKey,
|
|
340
|
+
transport: TinyTitleTransport,
|
|
341
|
+
requestId: string,
|
|
342
|
+
): Promise<{ generator: TextGenerationPipeline; device: TinyModelDevice }> {
|
|
343
|
+
const devices = tinyModelDeviceLoadOrder(tinyModelDevicePreference);
|
|
344
|
+
if (devices[0] !== tinyModelDevicePreference.device) {
|
|
345
|
+
sendLog(transport, "warn", "tiny-model: requested device is unsafe in the worker; using CPU", {
|
|
346
|
+
modelKey,
|
|
347
|
+
repo: spec.repo,
|
|
348
|
+
requestedDevice: tinyModelDevicePreference.device,
|
|
349
|
+
device: devices[0],
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
for (let i = 0; i < devices.length; i += 1) {
|
|
353
|
+
const device = devices[i]!;
|
|
354
|
+
try {
|
|
355
|
+
return {
|
|
356
|
+
generator: await loadPipelineOnDevice(transformers, spec, modelKey, transport, requestId, device),
|
|
357
|
+
device,
|
|
358
|
+
};
|
|
359
|
+
} catch (error) {
|
|
360
|
+
if (i === devices.length - 1) throw error;
|
|
361
|
+
const fallbackDevice = devices[i + 1]!;
|
|
362
|
+
sendLog(transport, "warn", "tiny-model: accelerated device failed; falling back", {
|
|
363
|
+
modelKey,
|
|
364
|
+
repo: spec.repo,
|
|
365
|
+
device,
|
|
366
|
+
fallbackDevice,
|
|
367
|
+
error: errorMessage(error),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
throw new Error("No tiny model devices configured");
|
|
372
|
+
}
|
|
373
|
+
|
|
307
374
|
async function loadPipeline(
|
|
308
375
|
modelKey: TinyLocalModelKey,
|
|
309
376
|
transport: TinyTitleTransport,
|
|
@@ -327,31 +394,28 @@ async function loadPipeline(
|
|
|
327
394
|
|
|
328
395
|
const transformers = await loadTransformers(transport, requestId, modelKey);
|
|
329
396
|
const startedAt = performance.now();
|
|
330
|
-
const loaded = transformers
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
throw error;
|
|
353
|
-
},
|
|
354
|
-
);
|
|
397
|
+
const loaded = loadPipelineWithDeviceFallback(transformers, spec, modelKey, transport, requestId).then(
|
|
398
|
+
({ generator, device }) => {
|
|
399
|
+
sendLog(transport, "debug", "tiny-model: local model loaded", {
|
|
400
|
+
modelKey,
|
|
401
|
+
repo: spec.repo,
|
|
402
|
+
device,
|
|
403
|
+
requestedDevice: tinyModelDevicePreference.device,
|
|
404
|
+
dtype: tinyModelDtypeOverride ?? spec.dtype,
|
|
405
|
+
elapsedMs: Math.round(performance.now() - startedAt),
|
|
406
|
+
});
|
|
407
|
+
transport.send({
|
|
408
|
+
type: "progress",
|
|
409
|
+
id: requestId,
|
|
410
|
+
event: { modelKey, status: "ready", task: "text-generation", model: spec.repo },
|
|
411
|
+
});
|
|
412
|
+
return generator;
|
|
413
|
+
},
|
|
414
|
+
error => {
|
|
415
|
+
pipelines.delete(modelKey);
|
|
416
|
+
throw error;
|
|
417
|
+
},
|
|
418
|
+
);
|
|
355
419
|
pipelines.set(modelKey, loaded);
|
|
356
420
|
return loaded;
|
|
357
421
|
}
|
|
@@ -411,7 +475,7 @@ function buildCompletionPrompt(generator: TextGenerationPipeline, promptText: st
|
|
|
411
475
|
* Generic single-turn completion used by Mnemosyne memory tasks (fact extraction
|
|
412
476
|
* and consolidation). The caller (Mnemosyne) supplies the full task prompt; we
|
|
413
477
|
* wrap it as the user turn, decode greedily, and return the raw text for the
|
|
414
|
-
* caller's own parser. Output is capped to keep
|
|
478
|
+
* caller's own parser. Output is capped to keep local inference latency bounded.
|
|
415
479
|
*/
|
|
416
480
|
async function generateCompletion(
|
|
417
481
|
transport: TinyTitleTransport,
|
package/src/tools/bash.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
ToolApprovalDecision,
|
|
8
8
|
} from "@oh-my-pi/pi-agent-core";
|
|
9
9
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
10
|
-
import { ImageProtocol, TERMINAL
|
|
10
|
+
import { ImageProtocol, TERMINAL } from "@oh-my-pi/pi-tui";
|
|
11
11
|
import { getProjectDir, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
12
12
|
import * as z from "zod/v4";
|
|
13
13
|
import { AsyncJobManager } from "../async";
|
|
@@ -15,6 +15,7 @@ import { type BashResult, executeBash } from "../exec/bash-executor";
|
|
|
15
15
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
16
16
|
import { InternalUrlRouter } from "../internal-urls";
|
|
17
17
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
18
|
+
import { shimmerEnabled } from "../modes/theme/shimmer";
|
|
18
19
|
import { highlightCode, type Theme } from "../modes/theme/theme";
|
|
19
20
|
import bashDescription from "../prompts/tools/bash.md" with { type: "text" };
|
|
20
21
|
import type { ClientBridgeTerminalExitStatus, ClientBridgeTerminalOutput } from "../session/client-bridge";
|
|
@@ -1045,15 +1046,6 @@ export function getBashEnvForDisplay(args: BashRenderArgs): Record<string, strin
|
|
|
1045
1046
|
return args.env ?? partialEnv;
|
|
1046
1047
|
}
|
|
1047
1048
|
|
|
1048
|
-
export function formatBashCommand(args: BashRenderArgs): string {
|
|
1049
|
-
const command = replaceTabs(args.command || "…");
|
|
1050
|
-
const prompt = "$";
|
|
1051
|
-
const cwd = getProjectDir();
|
|
1052
|
-
const displayWorkdir = formatToolWorkingDirectory(args.cwd, cwd);
|
|
1053
|
-
const renderedCommand = [formatBashEnvAssignments(getBashEnvForDisplay(args)), command].filter(Boolean).join(" ");
|
|
1054
|
-
return displayWorkdir ? `${prompt} cd ${displayWorkdir} && ${renderedCommand}` : `${prompt} ${renderedCommand}`;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
1049
|
/**
|
|
1058
1050
|
* Returns the bash command formatted for the result body: the dim `$ cd … &&`
|
|
1059
1051
|
* prefix joined with syntax-highlighted command lines. The prefix is applied
|
|
@@ -1088,10 +1080,20 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1088
1080
|
return {
|
|
1089
1081
|
renderCall(args: TArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
1090
1082
|
const renderArgs = toBashRenderArgs(args, config);
|
|
1091
|
-
const cmdText = formatBashCommand(renderArgs);
|
|
1092
1083
|
const title = config.resolveTitle(args, options);
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1084
|
+
const cmdLines = formatBashCommandLines(renderArgs, uiTheme);
|
|
1085
|
+
const header = renderStatusLine({ icon: "pending", title }, uiTheme);
|
|
1086
|
+
const outputBlock = new CachedOutputBlock();
|
|
1087
|
+
return {
|
|
1088
|
+
render: (width: number): string[] =>
|
|
1089
|
+
outputBlock.render(
|
|
1090
|
+
{ header, state: "pending", sections: [{ lines: cmdLines }], width, animate: true },
|
|
1091
|
+
uiTheme,
|
|
1092
|
+
),
|
|
1093
|
+
invalidate: () => {
|
|
1094
|
+
outputBlock.invalidate();
|
|
1095
|
+
},
|
|
1096
|
+
};
|
|
1095
1097
|
},
|
|
1096
1098
|
|
|
1097
1099
|
renderResult(
|
|
@@ -1204,6 +1206,7 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
1204
1206
|
{ label: uiTheme.fg("toolTitle", "Output"), lines: outputLines },
|
|
1205
1207
|
],
|
|
1206
1208
|
width,
|
|
1209
|
+
animate: options.isPartial && shimmerEnabled(),
|
|
1207
1210
|
},
|
|
1208
1211
|
uiTheme,
|
|
1209
1212
|
);
|
package/src/tools/eval.ts
CHANGED
|
@@ -10,10 +10,11 @@ import { defaultEvalSessionId } from "../eval/session-id";
|
|
|
10
10
|
import type { EvalCellResult, EvalDisplayOutput, EvalLanguage, EvalStatusEvent, EvalToolDetails } from "../eval/types";
|
|
11
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
12
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
13
|
+
import { shimmerEnabled } from "../modes/theme/shimmer";
|
|
13
14
|
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
14
15
|
import evalDescription from "../prompts/tools/eval.md" with { type: "text" };
|
|
15
16
|
import { DEFAULT_MAX_BYTES, OutputSink, type OutputSummary, TailBuffer } from "../session/streaming-output";
|
|
16
|
-
import { renderCodeCell } from "../tui";
|
|
17
|
+
import { borderShimmerTick, renderCodeCell } from "../tui";
|
|
17
18
|
import { formatDimensionNote, resizeImage } from "../utils/image-resize";
|
|
18
19
|
import { resolveEvalBackends, type ToolSession } from ".";
|
|
19
20
|
import { truncateForPrompt } from "./approval";
|
|
@@ -857,7 +858,7 @@ function formatCellOutputLines(
|
|
|
857
858
|
}
|
|
858
859
|
|
|
859
860
|
export const evalToolRenderer = {
|
|
860
|
-
renderCall(args: EvalRenderArgs,
|
|
861
|
+
renderCall(args: EvalRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
861
862
|
const cells = getRenderCells(args);
|
|
862
863
|
|
|
863
864
|
if (cells.length === 0) {
|
|
@@ -870,7 +871,8 @@ export const evalToolRenderer = {
|
|
|
870
871
|
|
|
871
872
|
return {
|
|
872
873
|
render: (width: number): string[] => {
|
|
873
|
-
const
|
|
874
|
+
const animate = options.isPartial && shimmerEnabled();
|
|
875
|
+
const key = `${animate ? borderShimmerTick() : 0}|${cells.map(c => `${c.language}:${c.title ?? ""}:${c.code.length}`).join("|")}`;
|
|
874
876
|
if (cached && cached.key === key && cached.width === width) {
|
|
875
877
|
return cached.result;
|
|
876
878
|
}
|
|
@@ -889,6 +891,7 @@ export const evalToolRenderer = {
|
|
|
889
891
|
width,
|
|
890
892
|
codeMaxLines: EVAL_DEFAULT_PREVIEW_LINES,
|
|
891
893
|
expanded: true,
|
|
894
|
+
animate,
|
|
892
895
|
},
|
|
893
896
|
uiTheme,
|
|
894
897
|
);
|
|
@@ -951,7 +954,8 @@ export const evalToolRenderer = {
|
|
|
951
954
|
render: (width: number): string[] => {
|
|
952
955
|
const expanded = options.renderContext?.expanded ?? options.expanded;
|
|
953
956
|
const previewLines = options.renderContext?.previewLines ?? EVAL_DEFAULT_PREVIEW_LINES;
|
|
954
|
-
const
|
|
957
|
+
const animate = options.isPartial && shimmerEnabled();
|
|
958
|
+
const key = `${expanded}|${previewLines}|${options.spinnerFrame}|${animate ? borderShimmerTick() : 0}`;
|
|
955
959
|
if (cached && cached.key === key && cached.width === width) {
|
|
956
960
|
return cached.result;
|
|
957
961
|
}
|
|
@@ -988,6 +992,7 @@ export const evalToolRenderer = {
|
|
|
988
992
|
codeMaxLines: expanded ? Number.POSITIVE_INFINITY : EVAL_DEFAULT_PREVIEW_LINES,
|
|
989
993
|
expanded,
|
|
990
994
|
width,
|
|
995
|
+
animate,
|
|
991
996
|
},
|
|
992
997
|
uiTheme,
|
|
993
998
|
);
|
package/src/tools/index.ts
CHANGED
|
@@ -43,7 +43,6 @@ import { MemoryReflectTool } from "./memory-reflect";
|
|
|
43
43
|
import { MemoryRetainTool } from "./memory-retain";
|
|
44
44
|
import { wrapToolWithMetaNotice } from "./output-meta";
|
|
45
45
|
import { ReadTool } from "./read";
|
|
46
|
-
import { RecipeTool } from "./recipe";
|
|
47
46
|
import { RenderMermaidTool } from "./render-mermaid";
|
|
48
47
|
import { createReportToolIssueTool, isAutoQaEnabled } from "./report-tool-issue";
|
|
49
48
|
import { ResolveTool } from "./resolve";
|
|
@@ -84,7 +83,6 @@ export * from "./memory-recall";
|
|
|
84
83
|
export * from "./memory-reflect";
|
|
85
84
|
export * from "./memory-retain";
|
|
86
85
|
export * from "./read";
|
|
87
|
-
export * from "./recipe";
|
|
88
86
|
export * from "./render-mermaid";
|
|
89
87
|
export * from "./report-tool-issue";
|
|
90
88
|
export * from "./resolve";
|
|
@@ -300,7 +298,6 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
300
298
|
rewind: RewindTool.createIf,
|
|
301
299
|
task: s => TaskTool.create(s),
|
|
302
300
|
job: JobTool.createIf,
|
|
303
|
-
recipe: RecipeTool.createIf,
|
|
304
301
|
irc: IrcTool.createIf,
|
|
305
302
|
todo_write: s => new TodoWriteTool(s),
|
|
306
303
|
web_search: s => new WebSearchTool(s),
|
|
@@ -416,13 +413,6 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
416
413
|
) {
|
|
417
414
|
requestedTools.push("ast_edit");
|
|
418
415
|
}
|
|
419
|
-
if (
|
|
420
|
-
requestedTools.includes("bash") &&
|
|
421
|
-
!requestedTools.includes("recipe") &&
|
|
422
|
-
session.settings.get("recipe.enabled")
|
|
423
|
-
) {
|
|
424
|
-
requestedTools.push("recipe");
|
|
425
|
-
}
|
|
426
416
|
if (["hindsight", "mnemosyne"].includes(session.settings.get("memory.backend") ?? "")) {
|
|
427
417
|
for (const name of ["recall", "retain", "reflect"]) {
|
|
428
418
|
if (!requestedTools.includes(name)) requestedTools.push(name);
|
|
@@ -467,7 +457,6 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
467
457
|
if (!session.settings.get("async.enabled") && session.getAgentId?.() === MAIN_AGENT_ID) return false;
|
|
468
458
|
return true;
|
|
469
459
|
}
|
|
470
|
-
if (name === "recipe") return session.settings.get("recipe.enabled");
|
|
471
460
|
if (name === "retain" || name === "recall" || name === "reflect") {
|
|
472
461
|
return ["hindsight", "mnemosyne"].includes(session.settings.get("memory.backend") ?? "");
|
|
473
462
|
}
|
package/src/tools/read.ts
CHANGED
|
@@ -689,6 +689,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
689
689
|
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
690
690
|
IS_HL_MODE: displayMode.hashLines,
|
|
691
691
|
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
692
|
+
INSPECT_IMAGE_ENABLED: this.#inspectImageEnabled,
|
|
692
693
|
});
|
|
693
694
|
}
|
|
694
695
|
|
package/src/tools/renderers.ts
CHANGED
|
@@ -24,7 +24,6 @@ import { inspectImageToolRenderer } from "./inspect-image-renderer";
|
|
|
24
24
|
import { jobToolRenderer } from "./job";
|
|
25
25
|
import { recallToolRenderer, reflectToolRenderer, retainToolRenderer } from "./memory-render";
|
|
26
26
|
import { readToolRenderer } from "./read";
|
|
27
|
-
import { recipeToolRenderer } from "./recipe/render";
|
|
28
27
|
import { resolveToolRenderer } from "./resolve";
|
|
29
28
|
import { searchToolRenderer } from "./search";
|
|
30
29
|
import { searchToolBm25Renderer } from "./search-tool-bm25";
|
|
@@ -51,7 +50,6 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
51
50
|
ast_edit: astEditToolRenderer as ToolRenderer,
|
|
52
51
|
bash: bashToolRenderer as ToolRenderer,
|
|
53
52
|
browser: browserToolRenderer as ToolRenderer,
|
|
54
|
-
recipe: recipeToolRenderer as ToolRenderer,
|
|
55
53
|
debug: debugToolRenderer as ToolRenderer,
|
|
56
54
|
eval: evalToolRenderer as ToolRenderer,
|
|
57
55
|
edit: editToolRenderer as ToolRenderer,
|
package/src/tui/code-cell.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface CodeCellOptions {
|
|
|
26
26
|
outputMaxLines?: number;
|
|
27
27
|
codeMaxLines?: number;
|
|
28
28
|
expanded?: boolean;
|
|
29
|
+
/** Animate the cell border with a sweeping segment while pending/running. */
|
|
30
|
+
animate?: boolean;
|
|
29
31
|
width: number;
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -130,7 +132,10 @@ export function renderCodeCell(options: CodeCellOptions, theme: Theme): string[]
|
|
|
130
132
|
sections.push({ label: theme.fg("toolTitle", "Output"), lines: outputLines });
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
return renderOutputBlock(
|
|
135
|
+
return renderOutputBlock(
|
|
136
|
+
{ header: title, headerMeta: meta, state, sections, width, animate: options.animate },
|
|
137
|
+
theme,
|
|
138
|
+
);
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
export interface MarkdownCellOptions {
|