@oh-my-pi/pi-coding-agent 16.1.13 → 16.1.15

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.
Files changed (153) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/cli.js +5615 -2833
  3. package/dist/types/advisor/runtime.d.ts +3 -0
  4. package/dist/types/config/settings-schema.d.ts +64 -14
  5. package/dist/types/eval/__tests__/julia-prelude.test.d.ts +1 -0
  6. package/dist/types/eval/backend-helpers.d.ts +23 -0
  7. package/dist/types/eval/executor-base.d.ts +118 -0
  8. package/dist/types/eval/index.d.ts +2 -0
  9. package/dist/types/eval/jl/executor.d.ts +44 -0
  10. package/dist/types/eval/jl/index.d.ts +11 -0
  11. package/dist/types/eval/jl/kernel.d.ts +28 -0
  12. package/dist/types/eval/jl/prelude.d.ts +1 -0
  13. package/dist/types/eval/jl/runtime.d.ts +22 -0
  14. package/dist/types/eval/kernel-base.d.ts +105 -0
  15. package/dist/types/eval/py/kernel.d.ts +3 -61
  16. package/dist/types/eval/rb/executor.d.ts +77 -0
  17. package/dist/types/eval/rb/index.d.ts +11 -0
  18. package/dist/types/eval/rb/kernel.d.ts +31 -0
  19. package/dist/types/eval/rb/prelude.d.ts +1 -0
  20. package/dist/types/eval/rb/runtime.d.ts +23 -0
  21. package/dist/types/eval/runtime-env.d.ts +24 -0
  22. package/dist/types/eval/types.d.ts +3 -3
  23. package/dist/types/export/share.d.ts +8 -1
  24. package/dist/types/extensibility/extensions/runner.d.ts +2 -15
  25. package/dist/types/extensibility/hooks/loader.d.ts +1 -25
  26. package/dist/types/extensibility/session-handler-types.d.ts +23 -0
  27. package/dist/types/jsonrpc/message-framing.d.ts +35 -0
  28. package/dist/types/mcp/transports/stdio.d.ts +12 -1
  29. package/dist/types/mnemopi/embed-client.d.ts +7 -24
  30. package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -1
  31. package/dist/types/modes/components/custom-editor.d.ts +11 -0
  32. package/dist/types/modes/components/selector-helpers.d.ts +53 -0
  33. package/dist/types/modes/components/status-line/context-thresholds.d.ts +4 -3
  34. package/dist/types/modes/components/status-line/types.d.ts +1 -0
  35. package/dist/types/modes/interactive-mode.d.ts +0 -2
  36. package/dist/types/modes/theme/theme.d.ts +8 -1
  37. package/dist/types/modes/types.d.ts +0 -2
  38. package/dist/types/modes/utils/copy-targets.d.ts +1 -1
  39. package/dist/types/modes/utils/interactive-context-helpers.d.ts +14 -0
  40. package/dist/types/modes/utils/transcript-render-helpers.d.ts +54 -0
  41. package/dist/types/sdk.d.ts +1 -1
  42. package/dist/types/secrets/obfuscator.d.ts +3 -3
  43. package/dist/types/session/agent-session.d.ts +2 -2
  44. package/dist/types/stt/asr-client.d.ts +3 -29
  45. package/dist/types/subprocess/worker-client.d.ts +149 -0
  46. package/dist/types/subprocess/worker-runtime.d.ts +107 -0
  47. package/dist/types/tiny/title-client.d.ts +14 -34
  48. package/dist/types/tools/eval-backends.d.ts +6 -3
  49. package/dist/types/tools/eval-render.d.ts +6 -5
  50. package/dist/types/tools/eval.d.ts +13 -15
  51. package/dist/types/tools/index.d.ts +3 -3
  52. package/dist/types/tts/tts-client.d.ts +3 -28
  53. package/dist/types/tui/code-cell.d.ts +7 -0
  54. package/dist/types/utils/file-display-mode.d.ts +1 -1
  55. package/dist/types/utils/shell-snapshot.d.ts +10 -0
  56. package/dist/types/web/parallel.d.ts +6 -0
  57. package/dist/types/web/search/providers/perplexity.d.ts +17 -3
  58. package/package.json +12 -12
  59. package/src/advisor/__tests__/advisor.test.ts +114 -0
  60. package/src/advisor/runtime.ts +129 -1
  61. package/src/config/model-registry.ts +12 -4
  62. package/src/config/settings-schema.ts +74 -18
  63. package/src/config/settings.ts +5 -0
  64. package/src/dap/client.ts +13 -107
  65. package/src/eval/__tests__/julia-prelude.test.ts +77 -0
  66. package/src/eval/backend-helpers.ts +48 -0
  67. package/src/eval/executor-base.ts +425 -0
  68. package/src/eval/index.ts +2 -0
  69. package/src/eval/jl/executor.ts +540 -0
  70. package/src/eval/jl/index.ts +54 -0
  71. package/src/eval/jl/kernel.ts +235 -0
  72. package/src/eval/jl/prelude.jl +930 -0
  73. package/src/eval/jl/prelude.ts +3 -0
  74. package/src/eval/jl/runner.jl +634 -0
  75. package/src/eval/jl/runtime.ts +118 -0
  76. package/src/eval/js/index.ts +3 -14
  77. package/src/eval/kernel-base.ts +569 -0
  78. package/src/eval/py/executor.ts +43 -252
  79. package/src/eval/py/index.ts +9 -20
  80. package/src/eval/py/kernel.ts +29 -544
  81. package/src/eval/rb/executor.ts +504 -0
  82. package/src/eval/rb/index.ts +54 -0
  83. package/src/eval/rb/kernel.ts +230 -0
  84. package/src/eval/rb/prelude.rb +721 -0
  85. package/src/eval/rb/prelude.ts +3 -0
  86. package/src/eval/rb/runner.rb +474 -0
  87. package/src/eval/rb/runtime.ts +132 -0
  88. package/src/eval/runtime-env.ts +104 -0
  89. package/src/eval/types.ts +3 -3
  90. package/src/exec/bash-executor.ts +44 -0
  91. package/src/export/share.ts +51 -28
  92. package/src/extensibility/extensions/runner.ts +4 -11
  93. package/src/extensibility/hooks/loader.ts +3 -21
  94. package/src/extensibility/session-handler-types.ts +21 -0
  95. package/src/internal-urls/docs-index.generated.txt +1 -1
  96. package/src/jsonrpc/message-framing.ts +142 -0
  97. package/src/lsp/client.ts +13 -109
  98. package/src/mcp/transports/stdio.ts +20 -4
  99. package/src/mnemopi/embed-client.ts +43 -198
  100. package/src/modes/components/agent-dashboard.ts +17 -40
  101. package/src/modes/components/chat-transcript-builder.ts +18 -102
  102. package/src/modes/components/custom-editor.test.ts +22 -0
  103. package/src/modes/components/custom-editor.ts +29 -1
  104. package/src/modes/components/extensions/extension-dashboard.ts +4 -13
  105. package/src/modes/components/extensions/extension-list.ts +16 -44
  106. package/src/modes/components/footer.ts +4 -3
  107. package/src/modes/components/history-search.ts +4 -16
  108. package/src/modes/components/selector-helpers.ts +129 -0
  109. package/src/modes/components/settings-selector.ts +7 -5
  110. package/src/modes/components/status-line/component.ts +5 -1
  111. package/src/modes/components/status-line/context-thresholds.ts +11 -3
  112. package/src/modes/components/status-line/segments.ts +1 -1
  113. package/src/modes/components/status-line/types.ts +1 -0
  114. package/src/modes/components/tree-selector.ts +13 -18
  115. package/src/modes/controllers/command-controller.ts +1 -0
  116. package/src/modes/controllers/event-controller.ts +3 -9
  117. package/src/modes/controllers/input-controller.ts +32 -54
  118. package/src/modes/interactive-mode.ts +5 -7
  119. package/src/modes/theme/theme.ts +35 -0
  120. package/src/modes/types.ts +0 -2
  121. package/src/modes/utils/copy-targets.ts +3 -2
  122. package/src/modes/utils/interactive-context-helpers.ts +27 -0
  123. package/src/modes/utils/transcript-render-helpers.ts +157 -0
  124. package/src/modes/utils/ui-helpers.ts +21 -126
  125. package/src/prompts/system/system-prompt.md +5 -5
  126. package/src/prompts/tools/bash.md +2 -3
  127. package/src/prompts/tools/eval.md +6 -4
  128. package/src/prompts/tools/find.md +0 -4
  129. package/src/prompts/tools/read.md +1 -2
  130. package/src/prompts/tools/replace.md +1 -1
  131. package/src/sdk.ts +13 -7
  132. package/src/secrets/obfuscator.ts +3 -9
  133. package/src/session/agent-session.ts +42 -9
  134. package/src/slash-commands/builtin-registry.ts +2 -1
  135. package/src/stt/asr-client.ts +35 -215
  136. package/src/stt/asr-worker.ts +29 -181
  137. package/src/subprocess/worker-client.ts +297 -0
  138. package/src/subprocess/worker-runtime.ts +277 -0
  139. package/src/task/executor.ts +4 -4
  140. package/src/tiny/title-client.ts +53 -219
  141. package/src/tiny/worker.ts +29 -180
  142. package/src/tools/eval-backends.ts +10 -3
  143. package/src/tools/eval-render.ts +17 -8
  144. package/src/tools/eval.ts +187 -22
  145. package/src/tools/index.ts +51 -28
  146. package/src/tts/tts-client.ts +38 -206
  147. package/src/tts/tts-worker.ts +23 -97
  148. package/src/tui/code-cell.ts +12 -1
  149. package/src/utils/file-display-mode.ts +2 -3
  150. package/src/utils/shell-snapshot.ts +63 -1
  151. package/src/web/parallel.ts +43 -42
  152. package/src/web/search/providers/parallel.ts +10 -99
  153. package/src/web/search/providers/perplexity.ts +18 -6
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Subprocess-backed Ruby runner.
3
+ *
4
+ * Speaks NDJSON with `runner.rb` over stdin/stdout. One subprocess per kernel
5
+ * instance; sessions reuse a single subprocess across executions. Cancellation
6
+ * is delivered as SIGINT (clean interrupt, kernel state preserved) and escalates
7
+ * to a full shutdown only when the runner ignores it. Mirrors the Python kernel
8
+ * (eval/py/kernel.ts); the IPC loop, lifecycle, and display rendering are shared
9
+ * with it via BaseKernel.
10
+ */
11
+ import * as fs from "node:fs";
12
+ import * as os from "node:os";
13
+ import * as path from "node:path";
14
+ import { $flag, isBunTestRuntime, logger, Snowflake } from "@oh-my-pi/pi-utils";
15
+ import { $ } from "bun";
16
+ import { Settings } from "../../config/settings";
17
+ import { BaseKernel, getRemainingTimeMs, type KernelRuntimeEnv, type KernelStartOptions } from "../kernel-base";
18
+ import type { KernelDisplayOutput } from "../py/display";
19
+ import { hostHasInheritableConsole, shouldHideKernelWindow } from "../py/spawn-options";
20
+ import { RUBY_PRELUDE } from "./prelude";
21
+ import RUNNER_SCRIPT from "./runner.rb" with { type: "text" };
22
+ import {
23
+ enumerateRubyRuntimes,
24
+ filterEnv,
25
+ type RubyRuntime,
26
+ resolveExplicitRubyRuntime,
27
+ resolveRubyRuntime,
28
+ } from "./runtime";
29
+
30
+ export type { KernelExecuteResult, KernelRuntimeEnv, KernelShutdownResult } from "../kernel-base";
31
+ export type { KernelDisplayOutput, PythonStatusEvent } from "../py/display";
32
+ export { renderKernelDisplay } from "../py/display";
33
+
34
+ const TRACE_IPC = $flag("PI_RUBY_IPC_TRACE");
35
+
36
+ // Cache the runner script on disk so the subprocess loads it normally. Cached
37
+ // per script hash so installs don't race across versions.
38
+ const RUNNER_CACHE_DIR = path.join(os.tmpdir(), "omp-ruby-runner");
39
+ let RUNNER_SCRIPT_PATH: string | null = null;
40
+
41
+ async function ensureRunnerScript(): Promise<string> {
42
+ if (RUNNER_SCRIPT_PATH) return RUNNER_SCRIPT_PATH;
43
+ await fs.promises.mkdir(RUNNER_CACHE_DIR, { recursive: true });
44
+ const hash = Bun.hash(RUNNER_SCRIPT).toString(36);
45
+ const target = path.join(RUNNER_CACHE_DIR, `runner-${hash}.rb`);
46
+ if (!fs.existsSync(target)) {
47
+ await Bun.write(target, RUNNER_SCRIPT);
48
+ }
49
+ RUNNER_SCRIPT_PATH = target;
50
+ return target;
51
+ }
52
+
53
+ const SHUTDOWN_GRACE_MS = 1_000;
54
+ const STARTUP_TIMEOUT_MS = 10_000;
55
+ // How long to wait after SIGINT for the runner to emit `done` before escalating
56
+ // to a full subprocess shutdown so the host queue unblocks instead of hanging.
57
+ const INTERRUPT_ESCALATION_MS = 5_000;
58
+
59
+ export interface KernelExecuteOptions {
60
+ id?: string;
61
+ /** Runtime working directory applied immediately before this request executes. */
62
+ cwd?: string;
63
+ /** Managed runtime environment variables applied immediately before this request executes. */
64
+ env?: KernelRuntimeEnv;
65
+ signal?: AbortSignal;
66
+ onChunk?: (text: string) => Promise<void> | void;
67
+ onDisplay?: (output: KernelDisplayOutput) => Promise<void> | void;
68
+ timeoutMs?: number;
69
+ silent?: boolean;
70
+ storeHistory?: boolean;
71
+ }
72
+
73
+ export interface RubyKernelAvailability {
74
+ ok: boolean;
75
+ rubyPath?: string;
76
+ reason?: string;
77
+ /** The probed-working runtime, when one was found. */
78
+ runtime?: RubyRuntime;
79
+ }
80
+
81
+ // Cache successful probes per resolved cwd + explicit interpreter. Failures are
82
+ // not cached so installing Ruby mid-session is picked up on the next attempt.
83
+ const availabilityCache = new Map<string, Promise<RubyKernelAvailability>>();
84
+
85
+ export async function checkRubyKernelAvailability(cwd: string, interpreter?: string): Promise<RubyKernelAvailability> {
86
+ if (isBunTestRuntime() || $flag("PI_RUBY_SKIP_CHECK")) {
87
+ return { ok: true };
88
+ }
89
+ const resolvedCwd = path.resolve(cwd);
90
+ const key = `${resolvedCwd}\0${interpreter ?? ""}`;
91
+ const cached = availabilityCache.get(key);
92
+ if (cached) return await cached;
93
+ const probe = probeRubyKernelAvailability(resolvedCwd, interpreter);
94
+ availabilityCache.set(key, probe);
95
+ const result = await probe;
96
+ if (!result.ok && availabilityCache.get(key) === probe) {
97
+ availabilityCache.delete(key);
98
+ }
99
+ return result;
100
+ }
101
+
102
+ async function probeRubyKernelAvailability(cwd: string, interpreter?: string): Promise<RubyKernelAvailability> {
103
+ try {
104
+ const settings = await Settings.init();
105
+ const { env } = settings.getShellConfig();
106
+ const baseEnv = filterEnv(env);
107
+ const runtimes = enumerateRubyRuntimes(cwd, baseEnv, interpreter);
108
+ if (runtimes.length === 0) {
109
+ return { ok: false, reason: "Ruby executable not found on PATH" };
110
+ }
111
+ const failures: string[] = [];
112
+ for (const runtime of runtimes) {
113
+ try {
114
+ const probe = await $`${runtime.rubyPath} -e ${"exit 0"}`.quiet().nothrow().cwd(cwd).env(runtime.env);
115
+ if (probe.exitCode === 0) {
116
+ return { ok: true, rubyPath: runtime.rubyPath, runtime };
117
+ }
118
+ failures.push(`${runtime.rubyPath} (exit code ${probe.exitCode})`);
119
+ } catch (err) {
120
+ failures.push(`${runtime.rubyPath} (${err instanceof Error ? err.message : String(err)})`);
121
+ }
122
+ }
123
+ return {
124
+ ok: false,
125
+ rubyPath: runtimes[0].rubyPath,
126
+ reason: `No working Ruby interpreter found. Tried: ${failures.join("; ")}`,
127
+ };
128
+ } catch (err) {
129
+ return { ok: false, reason: err instanceof Error ? err.message : String(err) };
130
+ }
131
+ }
132
+
133
+ export class RubyKernel extends BaseKernel<KernelExecuteOptions> {
134
+ private constructor(id: string) {
135
+ super(id, {
136
+ languageName: "Ruby",
137
+ traceIpc: TRACE_IPC,
138
+ exitPayload: JSON.stringify({ type: "exit" }),
139
+ interruptEscalationMs: INTERRUPT_ESCALATION_MS,
140
+ shutdownGraceMs: SHUTDOWN_GRACE_MS,
141
+ buildPayload: (code, msgId, opts) =>
142
+ JSON.stringify({
143
+ id: msgId,
144
+ code,
145
+ cwd: opts?.cwd,
146
+ env: opts?.env,
147
+ silent: opts?.silent ?? false,
148
+ storeHistory: opts?.storeHistory ?? !(opts?.silent ?? false),
149
+ }),
150
+ });
151
+ }
152
+
153
+ static async start(options: KernelStartOptions): Promise<RubyKernel> {
154
+ const availability = await logger.time(
155
+ "RubyKernel.start:availabilityCheck",
156
+ checkRubyKernelAvailability,
157
+ options.cwd,
158
+ options.interpreter,
159
+ );
160
+ if (!availability.ok) {
161
+ throw new Error(availability.reason ?? "Ruby kernel unavailable");
162
+ }
163
+
164
+ // Reuse the interpreter the availability probe selected. The fallback
165
+ // computes a runtime only for the skip-check fast path (test runtime /
166
+ // PI_RUBY_SKIP_CHECK), where no candidate was probed.
167
+ let runtime = availability.runtime;
168
+ if (!runtime) {
169
+ const { env: shellEnv } = (await Settings.init()).getShellConfig();
170
+ runtime = options.interpreter
171
+ ? resolveExplicitRubyRuntime(options.interpreter, options.cwd, filterEnv(shellEnv))
172
+ : resolveRubyRuntime(options.cwd, filterEnv(shellEnv));
173
+ }
174
+ const spawnEnv: Record<string, string> = {};
175
+ for (const key in runtime.env) {
176
+ const value = runtime.env[key];
177
+ if (typeof value === "string") spawnEnv[key] = value;
178
+ }
179
+ for (const key in options.env) {
180
+ const value = options.env[key];
181
+ if (typeof value === "string") spawnEnv[key] = value;
182
+ }
183
+
184
+ const scriptPath = await ensureRunnerScript();
185
+ const kernel = new RubyKernel(Snowflake.next());
186
+
187
+ const proc = Bun.spawn([runtime.rubyPath, scriptPath], {
188
+ cwd: options.cwd,
189
+ env: spawnEnv,
190
+ stdin: "pipe",
191
+ stdout: "pipe",
192
+ stderr: "pipe",
193
+ windowsHide: shouldHideKernelWindow({
194
+ platform: process.platform,
195
+ hostHasInheritableConsole: hostHasInheritableConsole(),
196
+ }),
197
+ });
198
+ kernel.setProcess(proc);
199
+
200
+ const startup = { signal: options.signal, deadlineMs: options.deadlineMs };
201
+ const startupBudget = Math.min(getRemainingTimeMs(startup.deadlineMs) ?? STARTUP_TIMEOUT_MS, STARTUP_TIMEOUT_MS);
202
+
203
+ try {
204
+ const initScript = buildInitScript(options.cwd, options.env);
205
+ await kernel.executeWithBudget(initScript, startup.signal, startupBudget, "Ruby kernel init");
206
+ await kernel.executeWithBudget(RUBY_PRELUDE, startup.signal, startupBudget, "Ruby kernel prelude");
207
+ return kernel;
208
+ } catch (err) {
209
+ await kernel.shutdown({ timeoutMs: SHUTDOWN_GRACE_MS }).catch(() => {});
210
+ throw err;
211
+ }
212
+ }
213
+ }
214
+
215
+ function buildInitScript(cwd: string, env?: Record<string, string | undefined>): string {
216
+ const envPayload: Record<string, string> = {};
217
+ for (const key in env) {
218
+ const value = env[key];
219
+ if (value !== undefined) envPayload[key] = value;
220
+ }
221
+ // JSON string literals are valid Ruby string literals. Emit one
222
+ // `ENV["k"] = "v"` per key — a `{"k":"v"}` object literal would parse as a
223
+ // SYMBOL-keyed hash in Ruby (`:"k" => "v"`), which `ENV[]=` rejects.
224
+ const lines = [`__omp_init_cwd = ${JSON.stringify(cwd)}`, "Dir.chdir(__omp_init_cwd) rescue nil"];
225
+ for (const key in envPayload) {
226
+ lines.push(`ENV[${JSON.stringify(key)}] = ${JSON.stringify(envPayload[key])}`);
227
+ }
228
+ lines.push("$LOAD_PATH.delete(__omp_init_cwd)", "$LOAD_PATH.unshift(__omp_init_cwd)");
229
+ return lines.join("\n");
230
+ }