@oh-my-pi/pi-coding-agent 14.5.12 → 14.5.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 +45 -0
- package/package.json +18 -10
- package/src/cli/jupyter-cli.ts +1 -1
- package/src/commit/pipeline.ts +4 -3
- package/src/config/model-equivalence.ts +49 -16
- package/src/config/model-registry.ts +100 -25
- package/src/config/model-resolver.ts +29 -15
- package/src/config/settings-schema.ts +20 -6
- package/src/config/settings.ts +9 -8
- package/src/config.ts +18 -6
- package/src/eval/backend.ts +43 -0
- package/src/eval/eval.lark +43 -0
- package/src/eval/index.ts +5 -0
- package/src/eval/js/context-manager.ts +717 -0
- package/src/eval/js/executor.ts +131 -0
- package/src/eval/js/index.ts +46 -0
- package/src/eval/js/prelude.ts +2 -0
- package/src/eval/js/prelude.txt +84 -0
- package/src/eval/js/tool-bridge.ts +124 -0
- package/src/eval/parse.ts +337 -0
- package/src/{ipy → eval/py}/executor.ts +2 -180
- package/src/{ipy → eval/py}/gateway-coordinator.ts +2 -2
- package/src/eval/py/index.ts +58 -0
- package/src/{ipy → eval/py}/kernel.ts +9 -45
- package/src/{ipy → eval/py}/prelude.py +39 -227
- package/src/eval/types.ts +48 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +8 -10
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/lsp/client.ts +9 -0
- package/src/lsp/index.ts +395 -0
- package/src/lsp/types.ts +15 -4
- package/src/main.ts +35 -14
- package/src/mcp/manager.ts +22 -0
- package/src/mcp/oauth-flow.ts +1 -1
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/components/{python-execution.ts → eval-execution.ts} +11 -4
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/oauth-selector.ts +2 -1
- package/src/modes/components/tool-execution.ts +3 -4
- package/src/modes/controllers/command-controller.ts +28 -8
- package/src/modes/controllers/input-controller.ts +4 -4
- package/src/modes/controllers/selector-controller.ts +2 -1
- package/src/modes/interactive-mode.ts +4 -5
- package/src/modes/rpc/rpc-client.ts +9 -0
- package/src/modes/rpc/rpc-mode.ts +6 -0
- package/src/modes/rpc/rpc-types.ts +9 -0
- package/src/modes/types.ts +3 -3
- package/src/modes/utils/ui-helpers.ts +2 -2
- package/src/prompts/system/system-prompt.md +3 -3
- package/src/prompts/tools/eval.md +92 -0
- package/src/prompts/tools/lsp.md +7 -3
- package/src/sdk.ts +64 -35
- package/src/session/agent-session.ts +152 -46
- package/src/session/messages.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/system-prompt.ts +34 -66
- package/src/task/agents.ts +4 -5
- package/src/task/executor.ts +5 -9
- package/src/tools/archive-reader.ts +9 -3
- package/src/tools/browser/launch.ts +22 -0
- package/src/tools/browser/readable.ts +11 -6
- package/src/tools/browser/registry.ts +25 -244
- package/src/tools/browser/render.ts +1 -1
- package/src/tools/browser/tab-protocol.ts +101 -0
- package/src/tools/browser/tab-supervisor.ts +429 -0
- package/src/tools/browser/tab-worker-entry.ts +21 -0
- package/src/tools/browser/tab-worker.ts +1006 -0
- package/src/tools/browser.ts +17 -32
- package/src/tools/checkpoint.ts +2 -2
- package/src/tools/{python.ts → eval.ts} +324 -315
- package/src/tools/exit-plan-mode.ts +1 -1
- package/src/tools/image-gen.ts +2 -2
- package/src/tools/index.ts +62 -100
- package/src/tools/read.ts +0 -6
- package/src/tools/recipe/runners/pkg.ts +34 -32
- package/src/tools/renderers.ts +2 -2
- package/src/tools/resolve.ts +7 -2
- package/src/tools/todo-write.ts +0 -1
- package/src/tools/tool-timeouts.ts +2 -2
- package/src/tools/write.ts +8 -1
- package/src/utils/markit.ts +15 -7
- package/src/utils/tools-manager.ts +5 -5
- package/src/web/scrapers/crossref.ts +3 -3
- package/src/web/scrapers/devto.ts +1 -1
- package/src/web/scrapers/discourse.ts +5 -5
- package/src/web/scrapers/firefox-addons.ts +1 -1
- package/src/web/scrapers/flathub.ts +2 -2
- package/src/web/scrapers/gitlab.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
- package/src/web/scrapers/mastodon.ts +9 -9
- package/src/web/scrapers/mdn.ts +11 -7
- package/src/web/scrapers/pub-dev.ts +1 -1
- package/src/web/scrapers/rawg.ts +3 -3
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/spdx.ts +1 -1
- package/src/web/scrapers/stackoverflow.ts +2 -2
- package/src/web/scrapers/types.ts +53 -39
- package/src/web/scrapers/w3c.ts +1 -1
- package/src/web/search/index.ts +5 -5
- package/src/web/search/provider.ts +121 -39
- package/src/web/search/providers/gemini.ts +4 -4
- package/src/web/search/render.ts +2 -2
- package/src/ipy/modules.ts +0 -144
- package/src/prompts/tools/python.md +0 -57
- package/src/tools/browser/vm.ts +0 -792
- /package/src/{ipy → eval/py}/cancellation.ts +0 -0
- /package/src/{ipy → eval/py}/prelude.ts +0 -0
- /package/src/{ipy → eval/py}/runtime.ts +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { DEFAULT_MAX_BYTES, OutputSink } from "../../session/streaming-output";
|
|
2
|
+
import type { ToolSession } from "../../tools";
|
|
3
|
+
import { executeInVmContext, type JsDisplayOutput } from "./context-manager";
|
|
4
|
+
|
|
5
|
+
export interface JsExecutorOptions {
|
|
6
|
+
cwd?: string;
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
deadlineMs?: number;
|
|
9
|
+
onChunk?: (chunk: string) => Promise<void> | void;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
sessionId: string;
|
|
12
|
+
reset?: boolean;
|
|
13
|
+
sessionFile?: string;
|
|
14
|
+
artifactPath?: string;
|
|
15
|
+
artifactId?: string;
|
|
16
|
+
session: ToolSession;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface JsResult {
|
|
20
|
+
output: string;
|
|
21
|
+
exitCode: number | undefined;
|
|
22
|
+
cancelled: boolean;
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
artifactId?: string;
|
|
25
|
+
totalLines: number;
|
|
26
|
+
totalBytes: number;
|
|
27
|
+
outputLines: number;
|
|
28
|
+
outputBytes: number;
|
|
29
|
+
displayOutputs: JsDisplayOutput[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getExecutionTimeoutMs(options: Pick<JsExecutorOptions, "deadlineMs" | "timeoutMs">): number | undefined {
|
|
33
|
+
if (options.deadlineMs !== undefined) {
|
|
34
|
+
return Math.max(1, options.deadlineMs - Date.now());
|
|
35
|
+
}
|
|
36
|
+
return options.timeoutMs;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isAbortError(error: unknown): boolean {
|
|
40
|
+
return (
|
|
41
|
+
(error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) ||
|
|
42
|
+
(error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError"))
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function executeJs(code: string, options: JsExecutorOptions): Promise<JsResult> {
|
|
47
|
+
const displayOutputs: JsDisplayOutput[] = [];
|
|
48
|
+
const outputSink = new OutputSink({
|
|
49
|
+
artifactPath: options.artifactPath,
|
|
50
|
+
artifactId: options.artifactId,
|
|
51
|
+
spillThreshold: DEFAULT_MAX_BYTES,
|
|
52
|
+
onChunk: chunk => options.onChunk?.(chunk),
|
|
53
|
+
});
|
|
54
|
+
const timeoutMs = getExecutionTimeoutMs(options);
|
|
55
|
+
const timeoutSignal =
|
|
56
|
+
typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0
|
|
57
|
+
? AbortSignal.timeout(timeoutMs)
|
|
58
|
+
: undefined;
|
|
59
|
+
const signal =
|
|
60
|
+
options.signal && timeoutSignal
|
|
61
|
+
? AbortSignal.any([options.signal, timeoutSignal])
|
|
62
|
+
: (options.signal ?? timeoutSignal);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
await executeInVmContext({
|
|
66
|
+
sessionKey: options.sessionId,
|
|
67
|
+
sessionId: options.sessionId,
|
|
68
|
+
cwd: options.cwd ?? options.session.cwd,
|
|
69
|
+
session: options.session,
|
|
70
|
+
reset: options.reset,
|
|
71
|
+
code,
|
|
72
|
+
filename: `js-cell-${crypto.randomUUID()}.js`,
|
|
73
|
+
timeoutMs,
|
|
74
|
+
runState: {
|
|
75
|
+
signal,
|
|
76
|
+
onText: chunk => outputSink.push(chunk),
|
|
77
|
+
onDisplay: output => {
|
|
78
|
+
displayOutputs.push(output);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
const summary = await outputSink.dump();
|
|
83
|
+
return {
|
|
84
|
+
output: summary.output,
|
|
85
|
+
exitCode: 0,
|
|
86
|
+
cancelled: false,
|
|
87
|
+
truncated: summary.truncated,
|
|
88
|
+
artifactId: summary.artifactId,
|
|
89
|
+
totalLines: summary.totalLines,
|
|
90
|
+
totalBytes: summary.totalBytes,
|
|
91
|
+
outputLines: summary.outputLines,
|
|
92
|
+
outputBytes: summary.outputBytes,
|
|
93
|
+
displayOutputs,
|
|
94
|
+
};
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (signal?.aborted || isAbortError(error)) {
|
|
97
|
+
const timeoutReason = timeoutSignal?.aborted ? "Command timed out" : "";
|
|
98
|
+
if (timeoutReason) {
|
|
99
|
+
outputSink.push(timeoutReason);
|
|
100
|
+
}
|
|
101
|
+
const summary = await outputSink.dump();
|
|
102
|
+
return {
|
|
103
|
+
output: summary.output,
|
|
104
|
+
exitCode: undefined,
|
|
105
|
+
cancelled: true,
|
|
106
|
+
truncated: summary.truncated,
|
|
107
|
+
artifactId: summary.artifactId,
|
|
108
|
+
totalLines: summary.totalLines,
|
|
109
|
+
totalBytes: summary.totalBytes,
|
|
110
|
+
outputLines: summary.outputLines,
|
|
111
|
+
outputBytes: summary.outputBytes,
|
|
112
|
+
displayOutputs,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
116
|
+
outputSink.push(message);
|
|
117
|
+
const summary = await outputSink.dump();
|
|
118
|
+
return {
|
|
119
|
+
output: summary.output,
|
|
120
|
+
exitCode: 1,
|
|
121
|
+
cancelled: false,
|
|
122
|
+
truncated: summary.truncated,
|
|
123
|
+
artifactId: summary.artifactId,
|
|
124
|
+
totalLines: summary.totalLines,
|
|
125
|
+
totalBytes: summary.totalBytes,
|
|
126
|
+
outputLines: summary.outputLines,
|
|
127
|
+
outputBytes: summary.outputBytes,
|
|
128
|
+
displayOutputs,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ToolSession } from "../../tools";
|
|
2
|
+
import type { ExecutorBackend, ExecutorBackendExecOptions, ExecutorBackendResult } from "../backend";
|
|
3
|
+
import { executeJs } from "./executor";
|
|
4
|
+
|
|
5
|
+
const JS_SESSION_PREFIX = "js:";
|
|
6
|
+
|
|
7
|
+
function namespaceSessionId(sessionId: string): string {
|
|
8
|
+
return sessionId.startsWith(JS_SESSION_PREFIX) ? sessionId : `${JS_SESSION_PREFIX}${sessionId}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
id: "js",
|
|
13
|
+
label: "JavaScript",
|
|
14
|
+
highlightLang: "javascript",
|
|
15
|
+
|
|
16
|
+
async isAvailable(_session: ToolSession): Promise<boolean> {
|
|
17
|
+
return true;
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
async execute(code: string, opts: ExecutorBackendExecOptions): Promise<ExecutorBackendResult> {
|
|
21
|
+
const result = await executeJs(code, {
|
|
22
|
+
cwd: opts.cwd,
|
|
23
|
+
deadlineMs: opts.deadlineMs,
|
|
24
|
+
signal: opts.signal,
|
|
25
|
+
sessionId: namespaceSessionId(opts.sessionId),
|
|
26
|
+
sessionFile: opts.sessionFile,
|
|
27
|
+
reset: opts.reset,
|
|
28
|
+
artifactPath: opts.artifactPath,
|
|
29
|
+
artifactId: opts.artifactId,
|
|
30
|
+
onChunk: opts.onChunk,
|
|
31
|
+
session: opts.session,
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
output: result.output,
|
|
35
|
+
exitCode: result.exitCode,
|
|
36
|
+
cancelled: result.cancelled,
|
|
37
|
+
truncated: result.truncated,
|
|
38
|
+
artifactId: result.artifactId,
|
|
39
|
+
totalLines: result.totalLines,
|
|
40
|
+
totalBytes: result.totalBytes,
|
|
41
|
+
outputLines: result.outputLines,
|
|
42
|
+
outputBytes: result.outputBytes,
|
|
43
|
+
displayOutputs: result.displayOutputs,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
} satisfies ExecutorBackend;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
if (!globalThis.__omp_js_prelude_loaded__) {
|
|
2
|
+
globalThis.__omp_js_prelude_loaded__ = true;
|
|
3
|
+
|
|
4
|
+
const toOptions = value => (value && typeof value === "object" && !Array.isArray(value) ? value : {});
|
|
5
|
+
const callHelper = (name, ...args) => globalThis.__omp_helpers__[name](...args);
|
|
6
|
+
|
|
7
|
+
const read = (path, opts = {}) => callHelper("read", path, toOptions(opts));
|
|
8
|
+
const write = async (path, data) => callHelper("writeFile", path, data);
|
|
9
|
+
const append = (path, content) => callHelper("append", path, content);
|
|
10
|
+
const stat = path => callHelper("stat", path);
|
|
11
|
+
const grep = (pattern, path, opts = {}) => callHelper("grep", pattern, path, toOptions(opts));
|
|
12
|
+
const rgrep = (pattern, path = ".", opts = {}) => callHelper("rgrep", pattern, path, toOptions(opts));
|
|
13
|
+
const find = (pattern, path = ".", opts = {}) => callHelper("find", pattern, path, toOptions(opts));
|
|
14
|
+
const glob = (pattern, path = ".", opts = {}) => callHelper("glob", pattern, path, toOptions(opts));
|
|
15
|
+
const sort = (text, opts = {}) => callHelper("sortText", text, toOptions(opts));
|
|
16
|
+
const uniq = (text, opts = {}) => callHelper("uniqText", text, toOptions(opts));
|
|
17
|
+
const counter = (items, opts = {}) => callHelper("counter", items, toOptions(opts));
|
|
18
|
+
const sed = (path, pattern, repl, opts = {}) => callHelper("sed", path, pattern, repl, toOptions(opts));
|
|
19
|
+
const diff = (a, b) => callHelper("diff", a, b);
|
|
20
|
+
const tree = (path = ".", opts = {}) => callHelper("tree", path, toOptions(opts));
|
|
21
|
+
const run = (cmd, opts = {}) => callHelper("run", cmd, toOptions(opts));
|
|
22
|
+
const env = (key, value) => callHelper("env", key, value);
|
|
23
|
+
|
|
24
|
+
const tool = new Proxy(
|
|
25
|
+
{},
|
|
26
|
+
{
|
|
27
|
+
get(_target, prop) {
|
|
28
|
+
if (typeof prop !== "string") return undefined;
|
|
29
|
+
return async args => globalThis.__omp_call_tool__(prop, args ?? {});
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const output = async (...args) => {
|
|
35
|
+
let opts = {};
|
|
36
|
+
let ids = args;
|
|
37
|
+
if (args.length > 0) {
|
|
38
|
+
const last = args.at(-1);
|
|
39
|
+
if (last && typeof last === "object" && !Array.isArray(last)) {
|
|
40
|
+
opts = last;
|
|
41
|
+
ids = args.slice(0, -1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const reads = ids.map(id => tool.read({ path: `agent://${id}`, ...opts }));
|
|
45
|
+
const values = await Promise.all(reads);
|
|
46
|
+
return values.length === 1 ? values[0] : values;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const display = value => {
|
|
50
|
+
globalThis.__omp_display__(value);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const formatArgs = args => args.map(arg => (typeof arg === "string" ? arg : arg));
|
|
54
|
+
|
|
55
|
+
const consoleBridge = {
|
|
56
|
+
log: (...args) => globalThis.__omp_log__("log", ...formatArgs(args)),
|
|
57
|
+
info: (...args) => globalThis.__omp_log__("info", ...formatArgs(args)),
|
|
58
|
+
warn: (...args) => globalThis.__omp_log__("warn", ...formatArgs(args)),
|
|
59
|
+
error: (...args) => globalThis.__omp_log__("error", ...formatArgs(args)),
|
|
60
|
+
debug: (...args) => globalThis.__omp_log__("debug", ...formatArgs(args)),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
globalThis.console = consoleBridge;
|
|
64
|
+
globalThis.print = consoleBridge.log;
|
|
65
|
+
globalThis.display = display;
|
|
66
|
+
globalThis.tool = tool;
|
|
67
|
+
globalThis.output = output;
|
|
68
|
+
globalThis.read = read;
|
|
69
|
+
globalThis.write = write;
|
|
70
|
+
globalThis.append = append;
|
|
71
|
+
globalThis.stat = stat;
|
|
72
|
+
globalThis.grep = grep;
|
|
73
|
+
globalThis.rgrep = rgrep;
|
|
74
|
+
globalThis.find = find;
|
|
75
|
+
globalThis.glob = glob;
|
|
76
|
+
globalThis.sort = sort;
|
|
77
|
+
globalThis.uniq = uniq;
|
|
78
|
+
globalThis.counter = counter;
|
|
79
|
+
globalThis.sed = sed;
|
|
80
|
+
globalThis.diff = diff;
|
|
81
|
+
globalThis.tree = tree;
|
|
82
|
+
globalThis.run = run;
|
|
83
|
+
globalThis.env = env;
|
|
84
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { ToolSession } from "../../tools";
|
|
3
|
+
import { ToolError } from "../../tools/tool-errors";
|
|
4
|
+
|
|
5
|
+
export interface JsStatusEvent {
|
|
6
|
+
op: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ToolBridgeOptions {
|
|
11
|
+
session: ToolSession;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
emitStatus?: (event: JsStatusEvent) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ToolValue =
|
|
17
|
+
| string
|
|
18
|
+
| {
|
|
19
|
+
text: string;
|
|
20
|
+
details?: unknown;
|
|
21
|
+
images?: Array<{ mimeType: string; data: string }>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function getTool(session: ToolSession, name: string): AgentTool {
|
|
25
|
+
const tool = session.getToolByName?.(name);
|
|
26
|
+
if (!tool) {
|
|
27
|
+
throw new ToolError(`Unknown tool from js runtime: ${name}`);
|
|
28
|
+
}
|
|
29
|
+
return tool;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeArgs(args: unknown): unknown {
|
|
33
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
34
|
+
return args;
|
|
35
|
+
}
|
|
36
|
+
const record = { ...(args as Record<string, unknown>) };
|
|
37
|
+
if (record._i === undefined) {
|
|
38
|
+
record._i = "js prelude";
|
|
39
|
+
}
|
|
40
|
+
return record;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function summarizeToolResult(name: string, args: unknown, result: AgentToolResult, text: string): JsStatusEvent {
|
|
44
|
+
const record = (args && typeof args === "object" ? (args as Record<string, unknown>) : {}) as Record<
|
|
45
|
+
string,
|
|
46
|
+
unknown
|
|
47
|
+
>;
|
|
48
|
+
const details = (
|
|
49
|
+
result.details && typeof result.details === "object" ? (result.details as Record<string, unknown>) : {}
|
|
50
|
+
) as Record<string, unknown>;
|
|
51
|
+
|
|
52
|
+
switch (name) {
|
|
53
|
+
case "read":
|
|
54
|
+
return { op: "read", path: record.path, chars: text.length, preview: text.slice(0, 500) };
|
|
55
|
+
case "write":
|
|
56
|
+
return {
|
|
57
|
+
op: "write",
|
|
58
|
+
path: record.path,
|
|
59
|
+
chars: typeof record.content === "string" ? record.content.length : 0,
|
|
60
|
+
};
|
|
61
|
+
case "grep":
|
|
62
|
+
return {
|
|
63
|
+
op: "grep",
|
|
64
|
+
pattern: record.pattern,
|
|
65
|
+
path: record.path,
|
|
66
|
+
count: details.matchCount ?? undefined,
|
|
67
|
+
};
|
|
68
|
+
case "find":
|
|
69
|
+
return {
|
|
70
|
+
op: "find",
|
|
71
|
+
pattern: record.pattern,
|
|
72
|
+
count: details.fileCount ?? undefined,
|
|
73
|
+
matches: Array.isArray(details.files) ? details.files.slice(0, 20) : undefined,
|
|
74
|
+
};
|
|
75
|
+
case "bash":
|
|
76
|
+
return {
|
|
77
|
+
op: "run",
|
|
78
|
+
cmd: record.command,
|
|
79
|
+
code: typeof details.exitCode === "number" ? details.exitCode : undefined,
|
|
80
|
+
output: text.slice(0, 500),
|
|
81
|
+
};
|
|
82
|
+
default:
|
|
83
|
+
return { op: name, chars: text.length };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function callSessionTool(name: string, args: unknown, options: ToolBridgeOptions): Promise<ToolValue> {
|
|
88
|
+
const tool = getTool(options.session, name);
|
|
89
|
+
const normalizedArgs = normalizeArgs(args);
|
|
90
|
+
const toolCallId = `js-${name}-${crypto.randomUUID()}`;
|
|
91
|
+
try {
|
|
92
|
+
const result = await tool.execute(toolCallId, normalizedArgs, options.signal);
|
|
93
|
+
const textBlocks = result.content.filter(
|
|
94
|
+
(content): content is { type: "text"; text: string } =>
|
|
95
|
+
content.type === "text" && typeof content.text === "string",
|
|
96
|
+
);
|
|
97
|
+
const imageBlocks = result.content.filter(
|
|
98
|
+
(content): content is { type: "image"; mimeType: string; data: string } =>
|
|
99
|
+
content.type === "image" && typeof content.mimeType === "string" && typeof content.data === "string",
|
|
100
|
+
);
|
|
101
|
+
const text = textBlocks.map(block => block.text).join("");
|
|
102
|
+
options.emitStatus?.(summarizeToolResult(name, normalizedArgs, result, text));
|
|
103
|
+
if (result.details === undefined && imageBlocks.length === 0) {
|
|
104
|
+
return text;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
text,
|
|
108
|
+
details: result.details,
|
|
109
|
+
images:
|
|
110
|
+
imageBlocks.length > 0
|
|
111
|
+
? imageBlocks.map(block => ({
|
|
112
|
+
mimeType: block.mimeType,
|
|
113
|
+
data: block.data,
|
|
114
|
+
}))
|
|
115
|
+
: undefined,
|
|
116
|
+
};
|
|
117
|
+
} catch (error) {
|
|
118
|
+
options.emitStatus?.({
|
|
119
|
+
op: name,
|
|
120
|
+
error: error instanceof Error ? error.message : String(error),
|
|
121
|
+
});
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|