@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
package/src/dap/client.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import { isEnoent, logger, ptree } from "@oh-my-pi/pi-utils";
3
3
  import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
4
+ import { MessageFramer } from "../jsonrpc/message-framing";
4
5
  import { ToolAbortError } from "../tools/tool-errors";
5
6
  import type {
6
7
  DapCapabilities,
@@ -29,69 +30,6 @@ type DapReverseRequestHandler = (args: unknown) => unknown | Promise<unknown>;
29
30
 
30
31
  const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
31
32
 
32
- // Reused for all full decodes; each decode() resets state, so a single
33
- // instance is safe and avoids per-message TextDecoder allocation.
34
- const MESSAGE_DECODER = new TextDecoder("utf-8");
35
-
36
- /**
37
- * Locate the `\r\n\r\n` header terminator across the pending chunk list.
38
- * Returns the absolute byte index of the first `\r`, or -1 when not present.
39
- * Equivalent to scanning the contiguous concatenation of the chunks.
40
- */
41
- function findHeaderEndInChunks(chunks: Buffer[]): number {
42
- let global = 0;
43
- let b0 = -1;
44
- let b1 = -1;
45
- let b2 = -1;
46
- for (const chunk of chunks) {
47
- for (let i = 0; i < chunk.length; i++) {
48
- const b3 = chunk[i];
49
- if (b0 === 13 && b1 === 10 && b2 === 13 && b3 === 10) {
50
- return global - 3;
51
- }
52
- b0 = b1;
53
- b1 = b2;
54
- b2 = b3;
55
- global++;
56
- }
57
- }
58
- return -1;
59
- }
60
-
61
- /** Copy the byte range [from, to) out of the pending chunk list into one Buffer. */
62
- function copyChunkRange(chunks: Buffer[], from: number, to: number): Buffer {
63
- const out = Buffer.allocUnsafe(to - from);
64
- let global = 0;
65
- let written = 0;
66
- for (const chunk of chunks) {
67
- const chunkEnd = global + chunk.length;
68
- if (chunkEnd > from && global < to) {
69
- const start = Math.max(from, global) - global;
70
- const end = Math.min(to, chunkEnd) - global;
71
- chunk.copy(out, written, start, end);
72
- written += end - start;
73
- }
74
- global = chunkEnd;
75
- if (global >= to) break;
76
- }
77
- return out;
78
- }
79
-
80
- /** Drop the first `count` bytes from the pending chunk list in place. */
81
- function dropChunkFront(chunks: Buffer[], count: number): void {
82
- let removed = 0;
83
- while (chunks.length > 0) {
84
- const head = chunks[0];
85
- if (removed + head.length <= count) {
86
- removed += head.length;
87
- chunks.shift();
88
- } else {
89
- chunks[0] = head.subarray(count - removed);
90
- break;
91
- }
92
- }
93
- }
94
-
95
33
  async function writeMessage(sink: DapWriteSink, message: DapRequestMessage | DapResponseMessage): Promise<void> {
96
34
  const content = JSON.stringify(message);
97
35
  sink.write(`Content-Length: ${Buffer.byteLength(content, "utf-8")}\r\n\r\n`);
@@ -452,52 +390,25 @@ export class DapClient {
452
390
  this.#isReading = true;
453
391
  const reader = this.#readable.getReader();
454
392
 
455
- // Incoming bytes are buffered as a list of chunks and only joined when a
456
- // full message is framed (mirrors the LSP reader) — concatenating the
457
- // accumulator on every read is O(n^2) for messages spanning many reads.
458
- const pendingChunks: Buffer[] = [];
459
- let pendingLen = 0;
460
- if (this.#messageBuffer.length > 0) {
461
- pendingChunks.push(this.#messageBuffer);
462
- pendingLen = this.#messageBuffer.length;
463
- }
393
+ const framer = new MessageFramer(this.#messageBuffer);
464
394
 
465
395
  try {
466
396
  while (true) {
467
397
  const { done, value } = await reader.read();
468
398
  if (done) break;
469
399
 
470
- pendingChunks.push(Buffer.from(value));
471
- pendingLen += value.length;
400
+ framer.push(Buffer.from(value));
472
401
 
473
402
  // Drain every complete message currently buffered.
474
- while (true) {
475
- const headerEnd = findHeaderEndInChunks(pendingChunks);
476
- if (headerEnd === -1) break;
477
-
478
- const headerText = MESSAGE_DECODER.decode(copyChunkRange(pendingChunks, 0, headerEnd));
479
- const contentLengthMatch = headerText.match(/Content-Length: (\d+)/i);
480
- if (!contentLengthMatch) {
481
- // Non-protocol bytes (e.g. an adapter printing to stdout).
482
- // Drop past the bogus terminator and resync instead of
483
- // stalling on the same junk header forever.
484
- logger.warn("DAP framing resync: header block without Content-Length", {
485
- adapter: this.adapter.name,
486
- header: headerText.slice(0, 200),
487
- });
488
- dropChunkFront(pendingChunks, headerEnd + 4);
489
- pendingLen -= headerEnd + 4;
490
- continue;
491
- }
492
-
493
- const contentLength = Number.parseInt(contentLengthMatch[1], 10);
494
- const messageStart = headerEnd + 4; // Skip \r\n\r\n
495
- const messageEnd = messageStart + contentLength;
496
- if (pendingLen < messageEnd) break;
497
-
498
- const messageText = MESSAGE_DECODER.decode(copyChunkRange(pendingChunks, messageStart, messageEnd));
499
- dropChunkFront(pendingChunks, messageEnd);
500
- pendingLen -= messageEnd;
403
+ for (const messageText of framer.drain(headerText => {
404
+ // Non-protocol bytes (e.g. an adapter printing to stdout).
405
+ // Drop past the bogus terminator and resync instead of
406
+ // stalling on the same junk header forever.
407
+ logger.warn("DAP framing resync: header block without Content-Length", {
408
+ adapter: this.adapter.name,
409
+ header: headerText.slice(0, 200),
410
+ });
411
+ })) {
501
412
  this.#lastActivity = Date.now();
502
413
 
503
414
  // A malformed message must not kill the reader — later
@@ -523,12 +434,7 @@ export class DapClient {
523
434
  this.#rejectPendingRequests(new Error(`DAP connection closed: ${toErrorMessage(error)}`));
524
435
  } finally {
525
436
  // Persist any unparsed remainder so a restarted reader resumes mid-message.
526
- this.#messageBuffer =
527
- pendingChunks.length === 0
528
- ? Buffer.alloc(0)
529
- : pendingChunks.length === 1
530
- ? pendingChunks[0]
531
- : Buffer.concat(pendingChunks, pendingLen);
437
+ this.#messageBuffer = framer.remainder();
532
438
  reader.releaseLock();
533
439
  this.#isReading = false;
534
440
  }
@@ -0,0 +1,77 @@
1
+ import { afterEach, describe, expect, it } from "bun:test";
2
+ import * as path from "node:path";
3
+ import { $which, TempDir } from "@oh-my-pi/pi-utils";
4
+ import { disposeJuliaKernelSessionsByOwner, executeJulia } from "../jl/executor";
5
+
6
+ const HAS_JULIA = Boolean($which("julia"));
7
+ const OWNER_ID = "julia-prelude-tests";
8
+
9
+ describe.skipIf(!HAS_JULIA)("eval Julia prelude helpers", () => {
10
+ afterEach(async () => {
11
+ await disposeJuliaKernelSessionsByOwner(OWNER_ID);
12
+ });
13
+
14
+ it("supports tree keyword options and unified diff", async () => {
15
+ using tempDir = TempDir.createSync("@omp-eval-julia-helpers-");
16
+ await Bun.write(path.join(tempDir.path(), "a.txt"), "same\nold\n");
17
+ await Bun.write(path.join(tempDir.path(), "b.txt"), "same\nnew\n");
18
+ await Bun.write(path.join(tempDir.path(), "dir", "child.txt"), "child");
19
+
20
+ const result = await executeJulia(
21
+ `
22
+ d = diff("a.txt", "b.txt")
23
+ println("DIFF_DELETE=", occursin("-old", d))
24
+ println("DIFF_ADD=", occursin("+new", d))
25
+ t = tree(".", max_depth=2)
26
+ println("TREE_CHILD=", occursin("child.txt", t))
27
+ nothing
28
+ `,
29
+ {
30
+ cwd: tempDir.path(),
31
+ sessionId: `julia-prelude-diff:${crypto.randomUUID()}`,
32
+ kernelOwnerId: OWNER_ID,
33
+ reset: true,
34
+ },
35
+ );
36
+
37
+ expect(result.exitCode).toBe(0);
38
+ expect(result.output).toContain("DIFF_DELETE=true");
39
+ expect(result.output).toContain("DIFF_ADD=true");
40
+ expect(result.output).toContain("TREE_CHILD=true");
41
+ });
42
+
43
+ it("supports output ranges, JSON queries, metadata, and ANSI stripping", async () => {
44
+ using tempDir = TempDir.createSync("@omp-eval-julia-output-");
45
+ const artifactsDir = path.join(tempDir.path(), "session-artifacts");
46
+ await Bun.write(path.join(artifactsDir, "alpha.md"), "one\ntwo\nthree\nfour");
47
+ await Bun.write(path.join(artifactsDir, "json.md"), JSON.stringify({ items: [{ name: "a" }, { name: "b" }] }));
48
+ await Bun.write(path.join(artifactsDir, "ansi.md"), "\u001b[31mred\u001b[0m");
49
+
50
+ const result = await executeJulia(
51
+ `
52
+ println("RANGE=", replace(output("alpha", offset=2, limit=2), "\\n" => "|"))
53
+ println("QUERY=", output("json", query=".items[1].name"))
54
+ println("STRIPPED=", output("ansi", format="stripped"))
55
+ meta = output("alpha", format="json")
56
+ println("META=", meta["id"], ":", meta["char_count"] > 0)
57
+ multi = output("alpha", "json")
58
+ println("MULTI=", length(multi), ":", multi[1]["id"], ":", multi[2]["id"])
59
+ nothing
60
+ `,
61
+ {
62
+ cwd: tempDir.path(),
63
+ artifactsDir,
64
+ sessionId: `julia-prelude-output:${crypto.randomUUID()}`,
65
+ kernelOwnerId: OWNER_ID,
66
+ reset: true,
67
+ },
68
+ );
69
+
70
+ expect(result.exitCode).toBe(0);
71
+ expect(result.output).toContain("RANGE=two|three");
72
+ expect(result.output).toContain('QUERY="b"');
73
+ expect(result.output).toContain("STRIPPED=red");
74
+ expect(result.output).toContain("META=alpha:true");
75
+ expect(result.output).toContain("MULTI=2:alpha:json");
76
+ });
77
+ });
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Helpers shared by the per-language eval backend definitions (jl/js/py/rb
3
+ * index modules): session-id namespacing, settings access, and projection of
4
+ * executor results into the ExecutorBackend result shape.
5
+ */
6
+ import type { ToolSession } from "../tools";
7
+ import type { ExecutorBackendResult } from "./backend";
8
+ import type { EvalDisplayOutput } from "./types";
9
+
10
+ export function namespaceSessionId(sessionId: string, prefix: string): string {
11
+ return sessionId.startsWith(prefix) ? sessionId : `${prefix}${sessionId}`;
12
+ }
13
+
14
+ export function readSetting<T>(session: ToolSession, key: string): T | undefined {
15
+ const settings = session.settings as { get?: (key: string) => T | undefined } | undefined;
16
+ return settings?.get?.(key);
17
+ }
18
+
19
+ export function readInterpreterSetting(session: ToolSession, key: string): string | undefined {
20
+ const value = readSetting<unknown>(session, key);
21
+ return typeof value === "string" ? value.trim() || undefined : undefined;
22
+ }
23
+
24
+ export function toExecutorBackendResult(result: {
25
+ output: string;
26
+ exitCode: number | undefined;
27
+ cancelled: boolean;
28
+ truncated: boolean;
29
+ artifactId?: string | undefined;
30
+ totalLines: number;
31
+ totalBytes: number;
32
+ outputLines: number;
33
+ outputBytes: number;
34
+ displayOutputs: EvalDisplayOutput[];
35
+ }): ExecutorBackendResult {
36
+ return {
37
+ output: result.output,
38
+ exitCode: result.exitCode,
39
+ cancelled: result.cancelled,
40
+ truncated: result.truncated,
41
+ artifactId: result.artifactId,
42
+ totalLines: result.totalLines,
43
+ totalBytes: result.totalBytes,
44
+ outputLines: result.outputLines,
45
+ outputBytes: result.outputBytes,
46
+ displayOutputs: result.displayOutputs,
47
+ };
48
+ }