@oh-my-pi/pi-coding-agent 16.1.13 → 16.1.14
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 +27 -0
- package/dist/cli.js +5547 -2759
- package/dist/types/config/settings-schema.d.ts +44 -14
- package/dist/types/eval/__tests__/julia-prelude.test.d.ts +1 -0
- package/dist/types/eval/backend-helpers.d.ts +23 -0
- package/dist/types/eval/executor-base.d.ts +118 -0
- package/dist/types/eval/index.d.ts +2 -0
- package/dist/types/eval/jl/executor.d.ts +44 -0
- package/dist/types/eval/jl/index.d.ts +11 -0
- package/dist/types/eval/jl/kernel.d.ts +28 -0
- package/dist/types/eval/jl/prelude.d.ts +1 -0
- package/dist/types/eval/jl/runtime.d.ts +22 -0
- package/dist/types/eval/kernel-base.d.ts +105 -0
- package/dist/types/eval/py/kernel.d.ts +3 -61
- package/dist/types/eval/rb/executor.d.ts +77 -0
- package/dist/types/eval/rb/index.d.ts +11 -0
- package/dist/types/eval/rb/kernel.d.ts +31 -0
- package/dist/types/eval/rb/prelude.d.ts +1 -0
- package/dist/types/eval/rb/runtime.d.ts +23 -0
- package/dist/types/eval/runtime-env.d.ts +24 -0
- package/dist/types/eval/types.d.ts +3 -3
- package/dist/types/extensibility/extensions/runner.d.ts +2 -15
- package/dist/types/extensibility/hooks/loader.d.ts +1 -25
- package/dist/types/extensibility/session-handler-types.d.ts +23 -0
- package/dist/types/jsonrpc/message-framing.d.ts +35 -0
- package/dist/types/mnemopi/embed-client.d.ts +7 -24
- package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +11 -0
- package/dist/types/modes/components/selector-helpers.d.ts +53 -0
- package/dist/types/modes/interactive-mode.d.ts +0 -2
- package/dist/types/modes/theme/theme.d.ts +8 -1
- package/dist/types/modes/types.d.ts +0 -2
- package/dist/types/modes/utils/copy-targets.d.ts +1 -1
- package/dist/types/modes/utils/interactive-context-helpers.d.ts +14 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +54 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/stt/asr-client.d.ts +3 -29
- package/dist/types/subprocess/worker-client.d.ts +149 -0
- package/dist/types/subprocess/worker-runtime.d.ts +107 -0
- package/dist/types/tiny/title-client.d.ts +14 -34
- package/dist/types/tools/eval-backends.d.ts +6 -3
- package/dist/types/tools/eval-render.d.ts +6 -5
- package/dist/types/tools/eval.d.ts +13 -15
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tts/tts-client.d.ts +3 -28
- package/dist/types/tui/code-cell.d.ts +7 -0
- package/dist/types/utils/file-display-mode.d.ts +1 -1
- package/dist/types/web/parallel.d.ts +6 -0
- package/package.json +12 -12
- package/src/config/settings-schema.ts +50 -18
- package/src/config/settings.ts +5 -0
- package/src/dap/client.ts +13 -107
- package/src/eval/__tests__/julia-prelude.test.ts +77 -0
- package/src/eval/backend-helpers.ts +48 -0
- package/src/eval/executor-base.ts +425 -0
- package/src/eval/index.ts +2 -0
- package/src/eval/jl/executor.ts +540 -0
- package/src/eval/jl/index.ts +54 -0
- package/src/eval/jl/kernel.ts +235 -0
- package/src/eval/jl/prelude.jl +930 -0
- package/src/eval/jl/prelude.ts +3 -0
- package/src/eval/jl/runner.jl +634 -0
- package/src/eval/jl/runtime.ts +118 -0
- package/src/eval/js/index.ts +3 -14
- package/src/eval/kernel-base.ts +569 -0
- package/src/eval/py/executor.ts +43 -252
- package/src/eval/py/index.ts +9 -20
- package/src/eval/py/kernel.ts +29 -544
- package/src/eval/rb/executor.ts +504 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +230 -0
- package/src/eval/rb/prelude.rb +721 -0
- package/src/eval/rb/prelude.ts +3 -0
- package/src/eval/rb/runner.rb +474 -0
- package/src/eval/rb/runtime.ts +132 -0
- package/src/eval/runtime-env.ts +104 -0
- package/src/eval/types.ts +3 -3
- package/src/extensibility/extensions/runner.ts +4 -11
- package/src/extensibility/hooks/loader.ts +3 -21
- package/src/extensibility/session-handler-types.ts +21 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/jsonrpc/message-framing.ts +142 -0
- package/src/lsp/client.ts +13 -109
- package/src/mnemopi/embed-client.ts +43 -198
- package/src/modes/components/agent-dashboard.ts +17 -40
- package/src/modes/components/chat-transcript-builder.ts +18 -102
- package/src/modes/components/custom-editor.ts +19 -0
- package/src/modes/components/extensions/extension-dashboard.ts +4 -13
- package/src/modes/components/extensions/extension-list.ts +16 -44
- package/src/modes/components/history-search.ts +4 -16
- package/src/modes/components/selector-helpers.ts +129 -0
- package/src/modes/components/settings-selector.ts +7 -5
- package/src/modes/components/tree-selector.ts +13 -18
- package/src/modes/controllers/event-controller.ts +3 -9
- package/src/modes/controllers/input-controller.ts +32 -54
- package/src/modes/interactive-mode.ts +5 -7
- package/src/modes/theme/theme.ts +35 -0
- package/src/modes/types.ts +0 -2
- package/src/modes/utils/copy-targets.ts +3 -2
- package/src/modes/utils/interactive-context-helpers.ts +27 -0
- package/src/modes/utils/transcript-render-helpers.ts +157 -0
- package/src/modes/utils/ui-helpers.ts +21 -126
- package/src/prompts/system/system-prompt.md +10 -10
- package/src/prompts/tools/bash.md +0 -1
- package/src/prompts/tools/eval.md +6 -4
- package/src/prompts/tools/find.md +0 -4
- package/src/prompts/tools/read.md +1 -2
- package/src/prompts/tools/replace.md +1 -1
- package/src/prompts/tools/search.md +0 -1
- package/src/sdk.ts +13 -7
- package/src/session/agent-session.ts +9 -7
- package/src/stt/asr-client.ts +35 -215
- package/src/stt/asr-worker.ts +29 -181
- package/src/subprocess/worker-client.ts +297 -0
- package/src/subprocess/worker-runtime.ts +277 -0
- package/src/task/executor.ts +4 -4
- package/src/tiny/title-client.ts +53 -219
- package/src/tiny/worker.ts +29 -180
- package/src/tools/eval-backends.ts +10 -3
- package/src/tools/eval-render.ts +17 -8
- package/src/tools/eval.ts +187 -22
- package/src/tools/index.ts +51 -28
- package/src/tts/tts-client.ts +38 -206
- package/src/tts/tts-worker.ts +23 -97
- package/src/tui/code-cell.ts +12 -1
- package/src/utils/file-display-mode.ts +2 -3
- package/src/web/parallel.ts +43 -42
- package/src/web/search/providers/parallel.ts +10 -99
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subprocess-backed Julia runner.
|
|
3
|
+
*
|
|
4
|
+
* The IPC loop, lifecycle, and display rendering are shared with the Python and
|
|
5
|
+
* Ruby runners via BaseKernel; this module supplies the Julia binary, runner
|
|
6
|
+
* script, and the runner's TSV/Base64 wire protocol.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { $flag, Snowflake } from "@oh-my-pi/pi-utils";
|
|
12
|
+
import { $ } from "bun";
|
|
13
|
+
import { Settings } from "../../config/settings";
|
|
14
|
+
import { BaseKernel, getRemainingTimeMs, type KernelStartOptions } from "../kernel-base";
|
|
15
|
+
import type { KernelDisplayOutput } from "../py/display";
|
|
16
|
+
import { hostHasInheritableConsole, shouldHideKernelWindow } from "../py/spawn-options";
|
|
17
|
+
import { JULIA_PRELUDE } from "./prelude";
|
|
18
|
+
import RUNNER_SCRIPT from "./runner.jl" with { type: "text" };
|
|
19
|
+
import {
|
|
20
|
+
enumerateJuliaRuntimes,
|
|
21
|
+
filterEnv,
|
|
22
|
+
type JuliaRuntime,
|
|
23
|
+
resolveExplicitJuliaRuntime,
|
|
24
|
+
resolveJuliaRuntime,
|
|
25
|
+
} from "./runtime";
|
|
26
|
+
|
|
27
|
+
export type { KernelExecuteResult, KernelRuntimeEnv } from "../kernel-base";
|
|
28
|
+
export { renderKernelDisplay } from "../py/display";
|
|
29
|
+
export type { KernelDisplayOutput };
|
|
30
|
+
|
|
31
|
+
const TRACE_IPC = $flag("PI_JULIA_IPC_TRACE");
|
|
32
|
+
|
|
33
|
+
// Cache the runner script on disk so the subprocess loads it normally. Cached
|
|
34
|
+
// per script hash so installs don't race across versions.
|
|
35
|
+
const RUNNER_CACHE_DIR = path.join(os.tmpdir(), "omp-julia-runner");
|
|
36
|
+
let RUNNER_SCRIPT_PATH: string | null = null;
|
|
37
|
+
|
|
38
|
+
async function ensureRunnerScript(): Promise<string> {
|
|
39
|
+
if (RUNNER_SCRIPT_PATH) return RUNNER_SCRIPT_PATH;
|
|
40
|
+
await fs.promises.mkdir(RUNNER_CACHE_DIR, { recursive: true });
|
|
41
|
+
const hash = Bun.hash(RUNNER_SCRIPT).toString(36);
|
|
42
|
+
const target = path.join(RUNNER_CACHE_DIR, `runner-${hash}.jl`);
|
|
43
|
+
if (!fs.existsSync(target)) {
|
|
44
|
+
await Bun.write(target, RUNNER_SCRIPT);
|
|
45
|
+
}
|
|
46
|
+
RUNNER_SCRIPT_PATH = target;
|
|
47
|
+
return target;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const SHUTDOWN_GRACE_MS = 1_000;
|
|
51
|
+
const STARTUP_TIMEOUT_MS = 15_000; // Julia compile/warmup can be slightly slower
|
|
52
|
+
const INTERRUPT_ESCALATION_MS = 5_000;
|
|
53
|
+
|
|
54
|
+
export interface KernelExecuteOptions {
|
|
55
|
+
id?: string;
|
|
56
|
+
cwd?: string;
|
|
57
|
+
env?: Record<string, string | undefined>;
|
|
58
|
+
silent?: boolean;
|
|
59
|
+
storeHistory?: boolean;
|
|
60
|
+
timeoutMs?: number;
|
|
61
|
+
signal?: AbortSignal;
|
|
62
|
+
onChunk?: (text: string) => void | Promise<void>;
|
|
63
|
+
onDisplay?: (output: KernelDisplayOutput) => void | Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface JuliaKernelAvailability {
|
|
67
|
+
ok: boolean;
|
|
68
|
+
juliaPath?: string;
|
|
69
|
+
runtime?: JuliaRuntime;
|
|
70
|
+
reason?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Cache successful probes per resolved cwd + explicit interpreter. Failures are
|
|
74
|
+
// not cached so installing Julia mid-session is picked up on the next attempt.
|
|
75
|
+
const availabilityCache = new Map<string, Promise<JuliaKernelAvailability>>();
|
|
76
|
+
|
|
77
|
+
export async function checkJuliaKernelAvailability(
|
|
78
|
+
cwd: string,
|
|
79
|
+
interpreter?: string,
|
|
80
|
+
): Promise<JuliaKernelAvailability> {
|
|
81
|
+
const cacheKey = `${path.resolve(cwd)}::${interpreter ?? ""}`;
|
|
82
|
+
let cached = availabilityCache.get(cacheKey);
|
|
83
|
+
if (!cached) {
|
|
84
|
+
cached = probeJuliaKernelAvailability(cwd, interpreter);
|
|
85
|
+
availabilityCache.set(cacheKey, cached);
|
|
86
|
+
}
|
|
87
|
+
const result = await cached;
|
|
88
|
+
if (!result.ok) {
|
|
89
|
+
availabilityCache.delete(cacheKey);
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function probeJuliaKernelAvailability(cwd: string, interpreter?: string): Promise<JuliaKernelAvailability> {
|
|
95
|
+
const { env: shellEnv } = (await Settings.init()).getShellConfig();
|
|
96
|
+
const baseEnv = filterEnv(shellEnv);
|
|
97
|
+
const runtimes = enumerateJuliaRuntimes(cwd, baseEnv, interpreter);
|
|
98
|
+
|
|
99
|
+
if (runtimes.length === 0) {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
reason: "Julia executable not found on PATH. Please install Julia (https://julialang.org/).",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const failures: string[] = [];
|
|
107
|
+
for (const runtime of runtimes) {
|
|
108
|
+
try {
|
|
109
|
+
const probe = await $`${runtime.juliaPath} -e "exit(0)"`.quiet().nothrow().cwd(cwd).env(runtime.env);
|
|
110
|
+
if (probe.exitCode === 0) {
|
|
111
|
+
return { ok: true, juliaPath: runtime.juliaPath, runtime };
|
|
112
|
+
}
|
|
113
|
+
failures.push(`${runtime.juliaPath} (exit code ${probe.exitCode})`);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
failures.push(`${runtime.juliaPath} (${err instanceof Error ? err.message : String(err)})`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
juliaPath: runtimes[0].juliaPath,
|
|
122
|
+
reason: `No working Julia interpreter found. Tried: ${failures.join("; ")}`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export class JuliaKernel extends BaseKernel<KernelExecuteOptions> {
|
|
127
|
+
private constructor(id: string) {
|
|
128
|
+
super(id, {
|
|
129
|
+
languageName: "Julia",
|
|
130
|
+
traceIpc: TRACE_IPC,
|
|
131
|
+
exitPayload: "exit",
|
|
132
|
+
interruptEscalationMs: INTERRUPT_ESCALATION_MS,
|
|
133
|
+
shutdownGraceMs: SHUTDOWN_GRACE_MS,
|
|
134
|
+
buildPayload: (code, msgId, opts) => {
|
|
135
|
+
// Convert arguments into a TSV / Base64 payload.
|
|
136
|
+
const cwdB64 = Buffer.from(opts?.cwd ?? "").toString("base64");
|
|
137
|
+
const silentVal = opts?.silent ? "1" : "0";
|
|
138
|
+
const storeHistVal = opts?.storeHistory !== false && !opts?.silent ? "1" : "0";
|
|
139
|
+
|
|
140
|
+
// Format environment variables as key1_b64:val1_b64 key2_b64:val2_b64
|
|
141
|
+
const envPairs: string[] = [];
|
|
142
|
+
if (opts?.env) {
|
|
143
|
+
for (const key in opts.env) {
|
|
144
|
+
const val = opts.env[key];
|
|
145
|
+
if (val !== undefined) {
|
|
146
|
+
const k_b64 = Buffer.from(key).toString("base64");
|
|
147
|
+
const v_b64 = Buffer.from(val).toString("base64");
|
|
148
|
+
envPairs.push(`${k_b64}:${v_b64}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const envPairsStr = envPairs.join(" ");
|
|
153
|
+
const codeB64 = Buffer.from(code).toString("base64");
|
|
154
|
+
|
|
155
|
+
return `run\t${msgId}\t${cwdB64}\t${silentVal}\t${storeHistVal}\t${envPairsStr}\t${codeB64}`;
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static async start(options: KernelStartOptions): Promise<JuliaKernel> {
|
|
161
|
+
const availability = await checkJuliaKernelAvailability(options.cwd, options.interpreter);
|
|
162
|
+
if (!availability.ok) {
|
|
163
|
+
throw new Error(availability.reason ?? "Julia kernel unavailable");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let runtime = availability.runtime;
|
|
167
|
+
if (!runtime) {
|
|
168
|
+
const { env: shellEnv } = (await Settings.init()).getShellConfig();
|
|
169
|
+
runtime = options.interpreter
|
|
170
|
+
? resolveExplicitJuliaRuntime(options.interpreter, options.cwd, filterEnv(shellEnv))
|
|
171
|
+
: resolveJuliaRuntime(options.cwd, filterEnv(shellEnv));
|
|
172
|
+
}
|
|
173
|
+
const spawnEnv: Record<string, string> = {};
|
|
174
|
+
for (const key in runtime.env) {
|
|
175
|
+
const value = runtime.env[key];
|
|
176
|
+
if (typeof value === "string") spawnEnv[key] = value;
|
|
177
|
+
}
|
|
178
|
+
for (const key in options.env) {
|
|
179
|
+
const value = options.env[key];
|
|
180
|
+
if (typeof value === "string") spawnEnv[key] = value;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const scriptPath = await ensureRunnerScript();
|
|
184
|
+
const kernel = new JuliaKernel(Snowflake.next());
|
|
185
|
+
|
|
186
|
+
const proc = Bun.spawn(
|
|
187
|
+
[runtime.juliaPath, "--startup-file=no", "--history-file=no", "--color=no", "--project=@.", scriptPath],
|
|
188
|
+
{
|
|
189
|
+
cwd: options.cwd,
|
|
190
|
+
env: spawnEnv,
|
|
191
|
+
stdin: "pipe",
|
|
192
|
+
stdout: "pipe",
|
|
193
|
+
stderr: "pipe",
|
|
194
|
+
windowsHide: shouldHideKernelWindow({
|
|
195
|
+
platform: process.platform,
|
|
196
|
+
hostHasInheritableConsole: hostHasInheritableConsole(),
|
|
197
|
+
}),
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
kernel.setProcess(proc);
|
|
201
|
+
|
|
202
|
+
const startup = { signal: options.signal, deadlineMs: options.deadlineMs };
|
|
203
|
+
const startupBudget = Math.min(getRemainingTimeMs(startup.deadlineMs) ?? STARTUP_TIMEOUT_MS, STARTUP_TIMEOUT_MS);
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const initScript = buildInitScript(options.cwd, options.env);
|
|
207
|
+
await kernel.executeWithBudget(initScript, startup.signal, startupBudget, "Julia kernel init");
|
|
208
|
+
await kernel.executeWithBudget(JULIA_PRELUDE, startup.signal, startupBudget, "Julia kernel prelude");
|
|
209
|
+
return kernel;
|
|
210
|
+
} catch (err) {
|
|
211
|
+
await kernel.shutdown({ timeoutMs: SHUTDOWN_GRACE_MS }).catch(() => {});
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function buildInitScript(cwd: string, env?: Record<string, string | undefined>): string {
|
|
218
|
+
const envPayload: Record<string, string> = {};
|
|
219
|
+
for (const key in env) {
|
|
220
|
+
const value = env[key];
|
|
221
|
+
if (value !== undefined) envPayload[key] = value;
|
|
222
|
+
}
|
|
223
|
+
const lines = [
|
|
224
|
+
`__omp_init_cwd = String(Base64.base64decode("${Buffer.from(cwd).toString("base64")}"))`,
|
|
225
|
+
"try cd(__omp_init_cwd) catch; end",
|
|
226
|
+
];
|
|
227
|
+
for (const key in envPayload) {
|
|
228
|
+
const k_b64 = Buffer.from(key).toString("base64");
|
|
229
|
+
const v_b64 = Buffer.from(envPayload[key]).toString("base64");
|
|
230
|
+
lines.push(`ENV[String(Base64.base64decode("${k_b64}"))] = String(Base64.base64decode("${v_b64}"))`);
|
|
231
|
+
}
|
|
232
|
+
// Avoid modifying LOAD_PATH if not necessary, but if needed, prepend cwd
|
|
233
|
+
lines.push("if !(__omp_init_cwd in LOAD_PATH); pushfirst!(LOAD_PATH, __omp_init_cwd); end");
|
|
234
|
+
return lines.join("\n");
|
|
235
|
+
}
|