@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
|
@@ -122,7 +122,7 @@ export const TAB_GROUPS: Record<SettingTab, readonly string[]> = {
|
|
|
122
122
|
context: ["General", "Compaction", "Rules (TTSR)", "Experimental"],
|
|
123
123
|
memory: ["General", "Auto-Learn", "Mnemopi", "Hindsight"],
|
|
124
124
|
files: ["Editing", "Reading", "Read Summaries", "LSP"],
|
|
125
|
-
shell: ["Bash", "Eval &
|
|
125
|
+
shell: ["Bash", "Eval & Runtimes"],
|
|
126
126
|
tools: [
|
|
127
127
|
"Available Tools",
|
|
128
128
|
"Todos",
|
|
@@ -2703,18 +2703,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
2703
2703
|
},
|
|
2704
2704
|
},
|
|
2705
2705
|
|
|
2706
|
-
readHashLines: {
|
|
2707
|
-
type: "boolean",
|
|
2708
|
-
default: true,
|
|
2709
|
-
ui: {
|
|
2710
|
-
tab: "files",
|
|
2711
|
-
group: "Reading",
|
|
2712
|
-
label: "Hash Lines",
|
|
2713
|
-
description:
|
|
2714
|
-
"Include snapshot-tag headers and line numbers in read output for hashline edit mode ([PATH#TAG] plus LINE:content)",
|
|
2715
|
-
},
|
|
2716
|
-
},
|
|
2717
|
-
|
|
2718
2706
|
"read.defaultLimit": {
|
|
2719
2707
|
type: "number",
|
|
2720
2708
|
default: 300,
|
|
@@ -2981,7 +2969,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2981
2969
|
default: true,
|
|
2982
2970
|
ui: {
|
|
2983
2971
|
tab: "shell",
|
|
2984
|
-
group: "Eval &
|
|
2972
|
+
group: "Eval & Runtimes",
|
|
2985
2973
|
label: "Python Eval Backend",
|
|
2986
2974
|
description: "Allow the eval tool to dispatch Python cells to the IPython kernel",
|
|
2987
2975
|
},
|
|
@@ -2992,20 +2980,42 @@ export const SETTINGS_SCHEMA = {
|
|
|
2992
2980
|
default: true,
|
|
2993
2981
|
ui: {
|
|
2994
2982
|
tab: "shell",
|
|
2995
|
-
group: "Eval &
|
|
2983
|
+
group: "Eval & Runtimes",
|
|
2996
2984
|
label: "JavaScript Eval Backend",
|
|
2997
2985
|
description: "Allow the eval tool to dispatch JavaScript cells to the in-process runtime",
|
|
2998
2986
|
},
|
|
2999
2987
|
},
|
|
3000
2988
|
|
|
3001
|
-
|
|
2989
|
+
"eval.rb": {
|
|
2990
|
+
type: "boolean",
|
|
2991
|
+
default: false,
|
|
2992
|
+
ui: {
|
|
2993
|
+
tab: "shell",
|
|
2994
|
+
group: "Eval & Runtimes",
|
|
2995
|
+
label: "Ruby Eval Backend",
|
|
2996
|
+
description: "Allow the eval tool to dispatch Ruby cells to the persistent Ruby kernel",
|
|
2997
|
+
},
|
|
2998
|
+
},
|
|
2999
|
+
|
|
3000
|
+
"eval.jl": {
|
|
3001
|
+
type: "boolean",
|
|
3002
|
+
default: false,
|
|
3003
|
+
ui: {
|
|
3004
|
+
tab: "shell",
|
|
3005
|
+
group: "Eval & Runtimes",
|
|
3006
|
+
label: "Julia Eval Backend",
|
|
3007
|
+
description: "Allow the eval tool to dispatch Julia cells to the persistent Julia kernel",
|
|
3008
|
+
},
|
|
3009
|
+
},
|
|
3010
|
+
|
|
3011
|
+
// Runtime knobs (consumed by eval backends and the /python slash command)
|
|
3002
3012
|
"python.kernelMode": {
|
|
3003
3013
|
type: "enum",
|
|
3004
3014
|
values: ["session", "per-call"] as const,
|
|
3005
3015
|
default: "session",
|
|
3006
3016
|
ui: {
|
|
3007
3017
|
tab: "shell",
|
|
3008
|
-
group: "Eval &
|
|
3018
|
+
group: "Eval & Runtimes",
|
|
3009
3019
|
label: "Python Kernel Mode",
|
|
3010
3020
|
description: "Keep the IPython kernel alive across eval calls or start fresh each time",
|
|
3011
3021
|
},
|
|
@@ -3015,12 +3025,34 @@ export const SETTINGS_SCHEMA = {
|
|
|
3015
3025
|
default: "",
|
|
3016
3026
|
ui: {
|
|
3017
3027
|
tab: "shell",
|
|
3018
|
-
group: "Eval &
|
|
3028
|
+
group: "Eval & Runtimes",
|
|
3019
3029
|
label: "Python Interpreter",
|
|
3020
3030
|
description:
|
|
3021
3031
|
"Optional path to an exact Python executable. When set, automatic Python runtime discovery is skipped.",
|
|
3022
3032
|
},
|
|
3023
3033
|
},
|
|
3034
|
+
"ruby.interpreter": {
|
|
3035
|
+
type: "string",
|
|
3036
|
+
default: "",
|
|
3037
|
+
ui: {
|
|
3038
|
+
tab: "shell",
|
|
3039
|
+
group: "Eval & Runtimes",
|
|
3040
|
+
label: "Ruby Interpreter",
|
|
3041
|
+
description:
|
|
3042
|
+
"Optional path to an exact Ruby executable. When set, automatic Ruby runtime discovery is skipped.",
|
|
3043
|
+
},
|
|
3044
|
+
},
|
|
3045
|
+
"julia.interpreter": {
|
|
3046
|
+
type: "string",
|
|
3047
|
+
default: "",
|
|
3048
|
+
ui: {
|
|
3049
|
+
tab: "shell",
|
|
3050
|
+
group: "Eval & Runtimes",
|
|
3051
|
+
label: "Julia Interpreter",
|
|
3052
|
+
description:
|
|
3053
|
+
"Optional path to an exact Julia executable. When set, automatic Julia runtime discovery is skipped.",
|
|
3054
|
+
},
|
|
3055
|
+
},
|
|
3024
3056
|
|
|
3025
3057
|
// ────────────────────────────────────────────────────────────────────────
|
|
3026
3058
|
// Tools
|
package/src/config/settings.ts
CHANGED
|
@@ -949,6 +949,11 @@ export class Settings {
|
|
|
949
949
|
delete raw["power.preventDisplaySleep"];
|
|
950
950
|
}
|
|
951
951
|
|
|
952
|
+
// readHashLines: removed. Hashline anchors are now driven solely by
|
|
953
|
+
// edit.mode === "hashline"; the separate read toggle only ever produced
|
|
954
|
+
// the incoherent "hashline edits without addressable anchors" state.
|
|
955
|
+
delete raw.readHashLines;
|
|
956
|
+
|
|
952
957
|
return raw;
|
|
953
958
|
}
|
|
954
959
|
|
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
|
-
|
|
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
|
-
|
|
471
|
-
pendingLen += value.length;
|
|
400
|
+
framer.push(Buffer.from(value));
|
|
472
401
|
|
|
473
402
|
// Drain every complete message currently buffered.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
+
}
|