@oh-my-pi/pi-coding-agent 15.10.4 → 15.10.6
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 +74 -0
- package/dist/types/capability/rule-buckets.d.ts +1 -1
- package/dist/types/capability/rule.d.ts +6 -1
- package/dist/types/cli/update-cli.d.ts +11 -1
- package/dist/types/config/model-registry.d.ts +18 -1
- package/dist/types/discovery/at-imports.d.ts +15 -0
- package/dist/types/edit/diff.d.ts +3 -2
- package/dist/types/eval/__tests__/helpers-local-roots.test.d.ts +1 -0
- package/dist/types/eval/backend.d.ts +7 -0
- package/dist/types/eval/js/context-manager.d.ts +1 -0
- package/dist/types/eval/js/executor.d.ts +2 -0
- package/dist/types/eval/js/index.d.ts +1 -1
- package/dist/types/eval/js/shared/helpers.d.ts +6 -0
- package/dist/types/eval/js/shared/runtime.d.ts +5 -0
- package/dist/types/eval/js/worker-protocol.d.ts +6 -0
- package/dist/types/eval/py/executor.d.ts +7 -0
- package/dist/types/eval/py/index.d.ts +1 -1
- package/dist/types/exa/index.d.ts +1 -19
- package/dist/types/exa/mcp-client.d.ts +10 -3
- package/dist/types/exa/types.d.ts +0 -83
- package/dist/types/export/ttsr.d.ts +14 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +1 -1
- package/dist/types/internal-urls/local-protocol.d.ts +10 -0
- package/dist/types/mcp/oauth-flow.d.ts +2 -2
- package/dist/types/modes/components/custom-editor.d.ts +3 -0
- package/dist/types/modes/components/{status-line.d.ts → status-line/component.d.ts} +2 -32
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +31 -2
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +8 -0
- package/dist/types/modes/image-references.d.ts +8 -3
- package/dist/types/modes/interactive-mode.d.ts +9 -1
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +0 -2
- package/dist/types/task/render.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +15 -0
- package/dist/types/tools/index.d.ts +17 -2
- package/dist/types/tools/render-utils.d.ts +1 -1
- package/dist/types/tools/tool-timeouts.d.ts +1 -1
- package/dist/types/utils/block-context.d.ts +35 -0
- package/dist/types/utils/git.d.ts +6 -0
- package/dist/types/utils/image-loading.d.ts +12 -0
- package/package.json +29 -9
- package/src/capability/rule-buckets.ts +4 -2
- package/src/capability/rule.ts +10 -1
- package/src/cli/auth-broker-cli.ts +6 -7
- package/src/cli/auth-gateway-cli.ts +4 -3
- package/src/cli/list-models.ts +5 -0
- package/src/cli/update-cli.ts +138 -16
- package/src/commit/agentic/tools/split-commit.ts +8 -1
- package/src/config/model-provider-priority.ts +1 -0
- package/src/config/model-registry.ts +81 -2
- package/src/debug/index.ts +4 -8
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-rules/index.ts +4 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/helpers.ts +2 -1
- package/src/edit/diff.ts +114 -4
- package/src/edit/hashline/diff.ts +1 -1
- package/src/edit/hashline/execute.ts +1 -1
- package/src/edit/modes/patch.ts +6 -2
- package/src/edit/modes/replace.ts +1 -1
- package/src/edit/renderer.ts +12 -2
- package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
- package/src/eval/backend.ts +15 -0
- package/src/eval/js/context-manager.ts +4 -2
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/index.ts +7 -1
- package/src/eval/js/shared/helpers.ts +53 -6
- package/src/eval/js/shared/runtime.ts +8 -0
- package/src/eval/js/worker-core.ts +1 -0
- package/src/eval/js/worker-protocol.ts +6 -0
- package/src/eval/py/executor.ts +12 -0
- package/src/eval/py/index.ts +7 -1
- package/src/eval/py/prelude.py +43 -4
- package/src/eval/py/runner.py +1 -0
- package/src/exa/index.ts +1 -26
- package/src/exa/mcp-client.ts +10 -10
- package/src/exa/types.ts +0 -97
- package/src/export/ttsr.ts +122 -1
- package/src/extensibility/extensions/types.ts +8 -1
- package/src/extensibility/legacy-pi-ai-shim.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +6 -5
- package/src/goals/tools/goal-tool.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +13 -0
- package/src/lsp/render.ts +8 -6
- package/src/mcp/oauth-flow.ts +3 -3
- package/src/mcp/render.ts +7 -1
- package/src/modes/components/agent-dashboard.ts +6 -4
- package/src/modes/components/custom-editor.ts +12 -6
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/oauth-selector.ts +4 -4
- package/src/modes/components/read-tool-group.ts +10 -3
- package/src/modes/components/{status-line.ts → status-line/component.ts} +18 -40
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/types.ts +23 -8
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/transcript-container.ts +17 -10
- package/src/modes/components/user-message.ts +6 -3
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/event-controller.ts +8 -0
- package/src/modes/controllers/extension-ui-controller.ts +143 -127
- package/src/modes/controllers/input-controller.ts +60 -11
- package/src/modes/controllers/mcp-command-controller.ts +52 -17
- package/src/modes/controllers/selector-controller.ts +4 -11
- package/src/modes/controllers/ssh-command-controller.ts +2 -2
- package/src/modes/image-references.ts +13 -7
- package/src/modes/interactive-mode.ts +35 -3
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +3 -11
- package/src/modes/theme/theme.ts +95 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/ui-helpers.ts +14 -5
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/eval.md +4 -4
- package/src/sdk.ts +31 -14
- package/src/session/agent-session.ts +290 -196
- package/src/session/session-manager.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +9 -1
- package/src/system-prompt.ts +15 -9
- package/src/task/index.ts +9 -1
- package/src/task/render.ts +36 -14
- package/src/tools/ask.ts +14 -5
- package/src/tools/bash-interactive.ts +1 -1
- package/src/tools/bash.ts +14 -2
- package/src/tools/browser/render.ts +5 -2
- package/src/tools/browser/tab-worker.ts +211 -91
- package/src/tools/debug.ts +5 -2
- package/src/tools/eval-render.ts +6 -3
- package/src/tools/eval.ts +1 -1
- package/src/tools/gh-renderer.ts +29 -15
- package/src/tools/index.ts +32 -4
- package/src/tools/inspect-image-renderer.ts +12 -5
- package/src/tools/job.ts +9 -6
- package/src/tools/memory-render.ts +19 -5
- package/src/tools/read.ts +165 -18
- package/src/tools/render-utils.ts +3 -1
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +1 -1
- package/src/tools/ssh.ts +4 -1
- package/src/tools/todo.ts +8 -1
- package/src/tools/tool-timeouts.ts +1 -1
- package/src/tools/write.ts +1 -1
- package/src/tui/code-cell.ts +1 -1
- package/src/utils/block-context.ts +312 -0
- package/src/utils/git.ts +41 -0
- package/src/utils/image-loading.ts +31 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/render.ts +14 -6
- package/dist/types/exa/factory.d.ts +0 -13
- package/dist/types/exa/render.d.ts +0 -19
- package/dist/types/exa/researcher.d.ts +0 -9
- package/dist/types/exa/search.d.ts +0 -9
- package/dist/types/exa/websets.d.ts +0 -9
- package/src/exa/factory.ts +0 -60
- package/src/exa/render.ts +0 -244
- package/src/exa/researcher.ts +0 -36
- package/src/exa/search.ts +0 -47
- package/src/exa/websets.ts +0 -248
package/src/edit/renderer.ts
CHANGED
|
@@ -260,6 +260,7 @@ function renderEditHeader(
|
|
|
260
260
|
uiTheme: Theme,
|
|
261
261
|
options: {
|
|
262
262
|
icon: "pending" | "success" | "error";
|
|
263
|
+
iconOverride?: string;
|
|
263
264
|
spinnerFrame?: number;
|
|
264
265
|
op?: Operation;
|
|
265
266
|
rawPath: string;
|
|
@@ -279,8 +280,16 @@ function renderEditHeader(
|
|
|
279
280
|
const formatted = formatEditDescription(options.rawPath, uiTheme, descriptionOptions);
|
|
280
281
|
const suffix = `${options.statsSuffix ?? ""}${options.extraSuffix ?? ""}`;
|
|
281
282
|
const buildHeader = (description: string): string =>
|
|
282
|
-
renderStatusLine(
|
|
283
|
-
|
|
283
|
+
renderStatusLine(
|
|
284
|
+
{
|
|
285
|
+
icon: options.icon,
|
|
286
|
+
iconOverride: options.iconOverride,
|
|
287
|
+
spinnerFrame: options.spinnerFrame,
|
|
288
|
+
title,
|
|
289
|
+
description,
|
|
290
|
+
},
|
|
291
|
+
uiTheme,
|
|
292
|
+
) + suffix;
|
|
284
293
|
|
|
285
294
|
const header = buildHeader(formatted.description);
|
|
286
295
|
const overflow = visibleWidth(header) - editHeaderLabelBudget(width, uiTheme);
|
|
@@ -633,6 +642,7 @@ function renderSingleFileResult(
|
|
|
633
642
|
const statsSuffix = headerDiff ? formatDiffStatsSuffix(headerDiff, uiTheme) : "";
|
|
634
643
|
const header = renderEditHeader(width, uiTheme, {
|
|
635
644
|
icon: isError ? "error" : "success",
|
|
645
|
+
iconOverride: !isError && !options.isPartial ? uiTheme.styledSymbol("tool.edit", "accent") : undefined,
|
|
636
646
|
op,
|
|
637
647
|
rawPath,
|
|
638
648
|
rename,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { TempDir } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { createHelpers, type HelperContext } from "../js/shared/helpers";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The eval helpers (`read`/`write`/`append`) must substitute injected on-disk
|
|
8
|
+
* roots for internal-URL schemes. Without it, `write("local://x.md")` hits a
|
|
9
|
+
* stdlib `path.resolve` that collapses `local://` to `local:/`, creating a junk
|
|
10
|
+
* `local:` directory under the cwd instead of landing where `read local://x.md`
|
|
11
|
+
* resolves. These lock the substitution contract and its guards.
|
|
12
|
+
*/
|
|
13
|
+
function makeCtx(cwd: string, roots: Record<string, string>): HelperContext {
|
|
14
|
+
return {
|
|
15
|
+
cwd: () => cwd,
|
|
16
|
+
env: new Map(),
|
|
17
|
+
localRoots: () => roots,
|
|
18
|
+
emitStatus: () => {},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("eval js helpers internal-url resolution", () => {
|
|
23
|
+
it("writes, reads, and appends local:// under the injected root", async () => {
|
|
24
|
+
using tmp = TempDir.createSync("@eval-helpers-local-");
|
|
25
|
+
const root = path.join(tmp.path(), "local");
|
|
26
|
+
const helpers = createHelpers(makeCtx(tmp.path(), { local: root }));
|
|
27
|
+
|
|
28
|
+
const written = await helpers.writeFile("local://notes/merge-map.md", "hello");
|
|
29
|
+
expect(written).toBe(path.join(root, "notes", "merge-map.md"));
|
|
30
|
+
expect(await Bun.file(written).text()).toBe("hello");
|
|
31
|
+
expect(await helpers.read("local://notes/merge-map.md")).toBe("hello");
|
|
32
|
+
|
|
33
|
+
await helpers.append("local://notes/merge-map.md", " world");
|
|
34
|
+
expect(await helpers.read("local://notes/merge-map.md")).toBe("hello world");
|
|
35
|
+
|
|
36
|
+
// Regression: no literal `local:` directory created under the cwd.
|
|
37
|
+
expect(await Bun.file(path.join(tmp.path(), "local:")).exists()).toBe(false);
|
|
38
|
+
expect(await Bun.file(path.join(tmp.path(), "local:", "notes", "merge-map.md")).exists()).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("rejects traversal and schemes without an injected root", async () => {
|
|
42
|
+
using tmp = TempDir.createSync("@eval-helpers-guard-");
|
|
43
|
+
const helpers = createHelpers(makeCtx(tmp.path(), { local: path.join(tmp.path(), "local") }));
|
|
44
|
+
|
|
45
|
+
await expect(helpers.writeFile("local://../escape.md", "x")).rejects.toThrow(/traversal|escapes/i);
|
|
46
|
+
await expect(helpers.writeFile("memory://x.md", "x")).rejects.toThrow(/not supported/i);
|
|
47
|
+
await expect(helpers.read("https://example.com/page")).rejects.toThrow(/not supported/i);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("leaves plain relative and absolute paths resolving against the cwd", async () => {
|
|
51
|
+
using tmp = TempDir.createSync("@eval-helpers-plain-");
|
|
52
|
+
const helpers = createHelpers(makeCtx(tmp.path(), {}));
|
|
53
|
+
|
|
54
|
+
const rel = await helpers.writeFile("foo/bar.txt", "bar");
|
|
55
|
+
expect(rel).toBe(path.join(tmp.path(), "foo", "bar.txt"));
|
|
56
|
+
expect(await helpers.read("foo/bar.txt")).toBe("bar");
|
|
57
|
+
});
|
|
58
|
+
});
|
package/src/eval/backend.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildEvalUrlRoots, type LocalProtocolOptions } from "../internal-urls";
|
|
1
2
|
import type { ToolSession } from "../tools";
|
|
2
3
|
import type { EvalDisplayOutput, EvalLanguage, EvalStatusEvent } from "./types";
|
|
3
4
|
|
|
@@ -56,3 +57,17 @@ export interface ExecutorBackend {
|
|
|
56
57
|
/** Execute one cell. Caller invokes once per cell and aggregates results. */
|
|
57
58
|
execute(code: string, opts: ExecutorBackendExecOptions): Promise<ExecutorBackendResult>;
|
|
58
59
|
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the on-disk roots that the eval helpers substitute for internal-URL
|
|
63
|
+
* schemes (currently `local://`). Prefers the session's own
|
|
64
|
+
* {@link LocalProtocolOptions} — the exact mapping `read local://…` uses — so an
|
|
65
|
+
* eval `write("local://x")` and a later `read local://x` agree on the location.
|
|
66
|
+
*/
|
|
67
|
+
export function resolveEvalUrlRoots(session: ToolSession): Record<string, string> {
|
|
68
|
+
const options: LocalProtocolOptions = session.localProtocolOptions ?? {
|
|
69
|
+
getArtifactsDir: () => session.getArtifactsDir?.() ?? null,
|
|
70
|
+
getSessionId: () => session.getSessionId?.() ?? null,
|
|
71
|
+
};
|
|
72
|
+
return buildEvalUrlRoots(options);
|
|
73
|
+
}
|
|
@@ -68,6 +68,7 @@ export async function executeInVmContext(options: {
|
|
|
68
68
|
sessionId: string;
|
|
69
69
|
cwd: string;
|
|
70
70
|
session: ToolSession;
|
|
71
|
+
localRoots?: Record<string, string>;
|
|
71
72
|
reset?: boolean;
|
|
72
73
|
code: string;
|
|
73
74
|
filename: string;
|
|
@@ -100,7 +101,7 @@ export async function executeInVmContext(options: {
|
|
|
100
101
|
}
|
|
101
102
|
const session = await acquireSession(
|
|
102
103
|
options.sessionKey,
|
|
103
|
-
{ cwd: options.cwd, sessionId: options.sessionId },
|
|
104
|
+
{ cwd: options.cwd, sessionId: options.sessionId, localRoots: options.localRoots },
|
|
104
105
|
options.timeoutMs,
|
|
105
106
|
);
|
|
106
107
|
return await runOnce(session, options);
|
|
@@ -132,6 +133,7 @@ async function runOnce(
|
|
|
132
133
|
sessionId: string;
|
|
133
134
|
cwd: string;
|
|
134
135
|
session: ToolSession;
|
|
136
|
+
localRoots?: Record<string, string>;
|
|
135
137
|
code: string;
|
|
136
138
|
filename: string;
|
|
137
139
|
runState: VmRunState;
|
|
@@ -171,7 +173,7 @@ async function runOnce(
|
|
|
171
173
|
runId,
|
|
172
174
|
code: options.code,
|
|
173
175
|
filename: options.filename,
|
|
174
|
-
snapshot: { cwd: options.cwd, sessionId: options.sessionId },
|
|
176
|
+
snapshot: { cwd: options.cwd, sessionId: options.sessionId, localRoots: options.localRoots },
|
|
175
177
|
});
|
|
176
178
|
return await promise;
|
|
177
179
|
} finally {
|
package/src/eval/js/executor.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface JsExecutorOptions {
|
|
|
24
24
|
artifactPath?: string;
|
|
25
25
|
artifactId?: string;
|
|
26
26
|
session: ToolSession;
|
|
27
|
+
/** On-disk roots the helpers substitute for internal-URL schemes (e.g. `local://`). */
|
|
28
|
+
localRoots?: Record<string, string>;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export interface JsResult {
|
|
@@ -96,6 +98,7 @@ export async function executeJs(code: string, options: JsExecutorOptions): Promi
|
|
|
96
98
|
sessionId: options.sessionId,
|
|
97
99
|
cwd: options.cwd ?? options.session.cwd,
|
|
98
100
|
session: options.session,
|
|
101
|
+
localRoots: options.localRoots,
|
|
99
102
|
reset: options.reset,
|
|
100
103
|
code,
|
|
101
104
|
filename: `js-cell-${crypto.randomUUID()}.js`,
|
package/src/eval/js/index.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ToolSession } from "../../tools";
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
type ExecutorBackend,
|
|
4
|
+
type ExecutorBackendExecOptions,
|
|
5
|
+
type ExecutorBackendResult,
|
|
6
|
+
resolveEvalUrlRoots,
|
|
7
|
+
} from "../backend";
|
|
3
8
|
import { executeJs } from "./executor";
|
|
4
9
|
|
|
5
10
|
const JS_SESSION_PREFIX = "js:";
|
|
@@ -30,6 +35,7 @@ export default {
|
|
|
30
35
|
onChunk: opts.onChunk,
|
|
31
36
|
onStatus: opts.onStatus,
|
|
32
37
|
session: opts.session,
|
|
38
|
+
localRoots: resolveEvalUrlRoots(opts.session),
|
|
33
39
|
});
|
|
34
40
|
return {
|
|
35
41
|
output: result.output,
|
|
@@ -24,6 +24,12 @@ export interface HelperOptions {
|
|
|
24
24
|
export interface HelperContext {
|
|
25
25
|
cwd(): string;
|
|
26
26
|
env: Map<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* On-disk roots for internal-URL schemes the helpers accept (e.g.
|
|
29
|
+
* `{ local: "/…/artifacts/local" }`). A path like `local://x.md` is rewritten
|
|
30
|
+
* to `<root>/x.md` before any filesystem op; unknown schemes are rejected.
|
|
31
|
+
*/
|
|
32
|
+
localRoots(): Record<string, string>;
|
|
27
33
|
emitStatus(event: JsStatusEvent): void;
|
|
28
34
|
}
|
|
29
35
|
|
|
@@ -66,7 +72,7 @@ export function createHelpers(ctx: HelperContext): HelperBundle {
|
|
|
66
72
|
if (!isWriteData(data)) {
|
|
67
73
|
throw new ToolError("write() expects string, Blob, ArrayBuffer, or TypedArray data");
|
|
68
74
|
}
|
|
69
|
-
const filePath =
|
|
75
|
+
const filePath = resolveHelperPath(ctx, rawPath, "write");
|
|
70
76
|
if (typeof data === "string" || data instanceof Blob || data instanceof ArrayBuffer) {
|
|
71
77
|
await Bun.write(filePath, data);
|
|
72
78
|
} else {
|
|
@@ -76,7 +82,7 @@ export function createHelpers(ctx: HelperContext): HelperBundle {
|
|
|
76
82
|
return filePath;
|
|
77
83
|
},
|
|
78
84
|
append: async (rawPath, content) => {
|
|
79
|
-
const target =
|
|
85
|
+
const target = resolveHelperPath(ctx, rawPath, "write");
|
|
80
86
|
await Bun.write(
|
|
81
87
|
target,
|
|
82
88
|
`${await Bun.file(target)
|
|
@@ -202,19 +208,60 @@ function getMergedEnv(ctx: HelperContext): Record<string, string> {
|
|
|
202
208
|
return merged;
|
|
203
209
|
}
|
|
204
210
|
|
|
211
|
+
const INTERNAL_URL_RE = /^([a-z][a-z0-9+.-]*):\/\/(.*)$/i;
|
|
212
|
+
|
|
205
213
|
function resolvePath(ctx: HelperContext, value: string): string {
|
|
206
214
|
if (path.isAbsolute(value)) return path.normalize(value);
|
|
207
215
|
return path.resolve(ctx.cwd(), value);
|
|
208
216
|
}
|
|
209
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Map a raw helper path to an absolute filesystem path. Plain paths resolve
|
|
220
|
+
* against the cwd; an internal-URL whose scheme has an injected root (e.g.
|
|
221
|
+
* `local://`) is rewritten under that root; any other `scheme://` is rejected
|
|
222
|
+
* so we never silently create a literal `scheme:/` directory.
|
|
223
|
+
*/
|
|
224
|
+
function resolveHelperPath(ctx: HelperContext, rawPath: string, op: "read" | "write"): string {
|
|
225
|
+
const match = INTERNAL_URL_RE.exec(rawPath);
|
|
226
|
+
if (!match) return resolvePath(ctx, rawPath);
|
|
227
|
+
const scheme = match[1].toLowerCase();
|
|
228
|
+
const root = ctx.localRoots()[scheme];
|
|
229
|
+
if (!root) {
|
|
230
|
+
throw new ToolError(`Protocol paths are not supported by ${op}(): ${rawPath}`);
|
|
231
|
+
}
|
|
232
|
+
return resolveUnderRoot(scheme, root, match[2], rawPath);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Resolve an internal-URL relative path under its root, mirroring the host
|
|
236
|
+
* local-protocol handler: decode, reject absolute/traversal, confine to root. */
|
|
237
|
+
function resolveUnderRoot(scheme: string, root: string, rawRelative: string, rawPath: string): string {
|
|
238
|
+
let relative: string;
|
|
239
|
+
try {
|
|
240
|
+
relative = decodeURIComponent(rawRelative.replaceAll("\\", "/"));
|
|
241
|
+
} catch {
|
|
242
|
+
throw new ToolError(`Invalid URL encoding in ${scheme}:// path: ${rawPath}`);
|
|
243
|
+
}
|
|
244
|
+
const rootPath = path.resolve(root);
|
|
245
|
+
if (relative === "") return rootPath;
|
|
246
|
+
if (path.isAbsolute(relative)) {
|
|
247
|
+
throw new ToolError(`Absolute paths are not allowed in ${scheme}:// URLs: ${rawPath}`);
|
|
248
|
+
}
|
|
249
|
+
const normalized = path.normalize(relative);
|
|
250
|
+
if (normalized.startsWith("..") || normalized.includes("/../") || normalized.includes("/..")) {
|
|
251
|
+
throw new ToolError(`Path traversal (..) is not allowed in ${scheme}:// URLs: ${rawPath}`);
|
|
252
|
+
}
|
|
253
|
+
const resolved = path.resolve(rootPath, normalized);
|
|
254
|
+
if (resolved !== rootPath && !resolved.startsWith(`${rootPath}${path.sep}`)) {
|
|
255
|
+
throw new ToolError(`${scheme}:// path escapes its root: ${rawPath}`);
|
|
256
|
+
}
|
|
257
|
+
return resolved;
|
|
258
|
+
}
|
|
259
|
+
|
|
210
260
|
async function resolveRegularFile(
|
|
211
261
|
ctx: HelperContext,
|
|
212
262
|
rawPath: string,
|
|
213
263
|
): Promise<{ filePath: string; file: Bun.BunFile; size: number }> {
|
|
214
|
-
|
|
215
|
-
throw new ToolError(`Protocol paths are not supported by read(): ${rawPath}`);
|
|
216
|
-
}
|
|
217
|
-
const filePath = resolvePath(ctx, rawPath);
|
|
264
|
+
const filePath = resolveHelperPath(ctx, rawPath, "read");
|
|
218
265
|
const file = Bun.file(filePath);
|
|
219
266
|
const stat = await file.stat();
|
|
220
267
|
if (stat.isDirectory()) {
|
|
@@ -42,6 +42,11 @@ export interface RuntimeOptions {
|
|
|
42
42
|
* via `setRunScope()` instead.
|
|
43
43
|
*/
|
|
44
44
|
extraGlobals?: Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* On-disk roots the helpers substitute for internal-URL schemes (e.g.
|
|
47
|
+
* `{ local: "/…/artifacts/local" }`). Stable for the worker's lifetime.
|
|
48
|
+
*/
|
|
49
|
+
localRoots?: Record<string, string>;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
// Strict base64: characters from the standard alphabet plus optional `=` padding, and a
|
|
@@ -126,15 +131,18 @@ export class JsRuntime {
|
|
|
126
131
|
#env: Map<string, string>;
|
|
127
132
|
#als = new AsyncLocalStorage<RunContext>();
|
|
128
133
|
#moduleLoader: LocalModuleLoader;
|
|
134
|
+
#localRoots: Record<string, string>;
|
|
129
135
|
|
|
130
136
|
constructor(opts: RuntimeOptions) {
|
|
131
137
|
this.#cwd = opts.initialCwd;
|
|
132
138
|
this.sessionId = opts.sessionId;
|
|
133
139
|
this.#env = new Map();
|
|
134
140
|
this.#moduleLoader = new LocalModuleLoader(this.sessionId);
|
|
141
|
+
this.#localRoots = opts.localRoots ?? {};
|
|
135
142
|
this.helpers = createHelpers({
|
|
136
143
|
cwd: () => this.#activeCwd(),
|
|
137
144
|
env: this.#env,
|
|
145
|
+
localRoots: () => this.#localRoots,
|
|
138
146
|
emitStatus: event => this.#activeHooks("emitStatus")?.onDisplay({ type: "status", event }),
|
|
139
147
|
});
|
|
140
148
|
this.#install(opts.extraGlobals);
|
|
@@ -5,6 +5,12 @@ export type { JsDisplayOutput } from "./shared/types";
|
|
|
5
5
|
export interface SessionSnapshot {
|
|
6
6
|
cwd: string;
|
|
7
7
|
sessionId: string;
|
|
8
|
+
/**
|
|
9
|
+
* On-disk roots the helpers substitute for internal-URL schemes
|
|
10
|
+
* (e.g. `{ local: "/…/artifacts/local" }`). Lets `read`/`write`/`append`
|
|
11
|
+
* accept `local://…` paths instead of writing a literal `local:/` directory.
|
|
12
|
+
*/
|
|
13
|
+
localRoots?: Record<string, string>;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export interface RunErrorPayload {
|
package/src/eval/py/executor.ts
CHANGED
|
@@ -56,6 +56,13 @@ export interface PythonExecutorOptions {
|
|
|
56
56
|
/** Artifact path/id for full output storage */
|
|
57
57
|
artifactPath?: string;
|
|
58
58
|
artifactId?: string;
|
|
59
|
+
/**
|
|
60
|
+
* On-disk roots the prelude helpers (`read`/`write`/`append`) substitute for
|
|
61
|
+
* internal-URL schemes (e.g. `{ local: "/…/artifacts/local" }`). Exported to
|
|
62
|
+
* the kernel as `PI_EVAL_LOCAL_ROOTS` (JSON) so `write("local://x")` lands
|
|
63
|
+
* where `read local://x` resolves instead of a literal `local:/` directory.
|
|
64
|
+
*/
|
|
65
|
+
localRoots?: Record<string, string>;
|
|
59
66
|
/**
|
|
60
67
|
* ToolSession used to resolve host-side `tool.<name>(args)` calls made from
|
|
61
68
|
* the Python prelude's bridge proxy. When omitted, the bridge env vars are
|
|
@@ -275,6 +282,7 @@ const MANAGED_KERNEL_ENV_KEYS = [
|
|
|
275
282
|
"PI_TOOL_BRIDGE_URL",
|
|
276
283
|
"PI_TOOL_BRIDGE_TOKEN",
|
|
277
284
|
"PI_TOOL_BRIDGE_SESSION",
|
|
285
|
+
"PI_EVAL_LOCAL_ROOTS",
|
|
278
286
|
] as const;
|
|
279
287
|
|
|
280
288
|
function buildKernelEnvPatch(options: {
|
|
@@ -282,13 +290,16 @@ function buildKernelEnvPatch(options: {
|
|
|
282
290
|
artifactsDir?: string;
|
|
283
291
|
bridgeSessionId?: string;
|
|
284
292
|
bridge?: { url: string; token: string };
|
|
293
|
+
localRoots?: Record<string, string>;
|
|
285
294
|
}): KernelRuntimeEnv {
|
|
295
|
+
const localRoots = options.localRoots;
|
|
286
296
|
return {
|
|
287
297
|
PI_SESSION_FILE: options.sessionFile ?? null,
|
|
288
298
|
PI_ARTIFACTS_DIR: options.artifactsDir ?? null,
|
|
289
299
|
PI_TOOL_BRIDGE_URL: options.bridge?.url ?? null,
|
|
290
300
|
PI_TOOL_BRIDGE_TOKEN: options.bridge?.token ?? null,
|
|
291
301
|
PI_TOOL_BRIDGE_SESSION: options.bridge && options.bridgeSessionId ? options.bridgeSessionId : null,
|
|
302
|
+
PI_EVAL_LOCAL_ROOTS: localRoots && Object.keys(localRoots).length > 0 ? JSON.stringify(localRoots) : null,
|
|
292
303
|
};
|
|
293
304
|
}
|
|
294
305
|
|
|
@@ -297,6 +308,7 @@ function buildKernelEnv(options: {
|
|
|
297
308
|
artifactsDir?: string;
|
|
298
309
|
bridgeSessionId?: string;
|
|
299
310
|
bridge?: { url: string; token: string };
|
|
311
|
+
localRoots?: Record<string, string>;
|
|
300
312
|
}): Record<string, string> | undefined {
|
|
301
313
|
const patch = buildKernelEnvPatch(options);
|
|
302
314
|
const env: Record<string, string> = {};
|
package/src/eval/py/index.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ToolSession } from "../../tools";
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
type ExecutorBackend,
|
|
4
|
+
type ExecutorBackendExecOptions,
|
|
5
|
+
type ExecutorBackendResult,
|
|
6
|
+
resolveEvalUrlRoots,
|
|
7
|
+
} from "../backend";
|
|
3
8
|
import { executePython, type PythonExecutorOptions } from "./executor";
|
|
4
9
|
import { checkPythonKernelAvailability } from "./kernel";
|
|
5
10
|
|
|
@@ -34,6 +39,7 @@ export default {
|
|
|
34
39
|
kernelMode,
|
|
35
40
|
sessionFile: opts.sessionFile,
|
|
36
41
|
artifactsDir: opts.session.getArtifactsDir?.() ?? undefined,
|
|
42
|
+
localRoots: resolveEvalUrlRoots(opts.session),
|
|
37
43
|
kernelOwnerId: opts.kernelOwnerId,
|
|
38
44
|
reset: opts.reset,
|
|
39
45
|
artifactPath: opts.artifactPath,
|
package/src/eval/py/prelude.py
CHANGED
|
@@ -3,7 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
if "__omp_prelude_loaded__" not in globals():
|
|
4
4
|
__omp_prelude_loaded__ = True
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
import os, json, math
|
|
6
|
+
import os, json, math, re
|
|
7
|
+
from urllib.parse import unquote
|
|
7
8
|
|
|
8
9
|
# __omp_display is injected by runner.py before the prelude executes; it
|
|
9
10
|
# mirrors IPython's display() semantics with the same MIME bundle output.
|
|
@@ -53,9 +54,47 @@ if "__omp_prelude_loaded__" not in globals():
|
|
|
53
54
|
_emit_status("env", key=key, value=val, action="get")
|
|
54
55
|
return val
|
|
55
56
|
|
|
57
|
+
_OMP_INTERNAL_URL_RE = re.compile(r"^([a-z][a-z0-9+.-]*)://(.*)$", re.IGNORECASE)
|
|
58
|
+
|
|
59
|
+
def _resolve_omp_path(path: str | Path) -> Path:
|
|
60
|
+
"""Map a helper path to a real filesystem Path.
|
|
61
|
+
|
|
62
|
+
A `scheme://…` whose scheme has an injected on-disk root (e.g.
|
|
63
|
+
`local://`, via PI_EVAL_LOCAL_ROOTS) is rewritten under that root so it
|
|
64
|
+
lands where `read local://…` resolves — not a literal `local:/`
|
|
65
|
+
directory under the cwd (which `Path("local://x")` collapses to). Plain
|
|
66
|
+
paths pass through unchanged; any other `scheme://` is rejected."""
|
|
67
|
+
if not isinstance(path, str):
|
|
68
|
+
return Path(path)
|
|
69
|
+
match = _OMP_INTERNAL_URL_RE.match(path)
|
|
70
|
+
if not match:
|
|
71
|
+
return Path(path)
|
|
72
|
+
scheme = match.group(1).lower()
|
|
73
|
+
try:
|
|
74
|
+
roots = json.loads(os.environ.get("PI_EVAL_LOCAL_ROOTS") or "{}")
|
|
75
|
+
except (ValueError, TypeError):
|
|
76
|
+
roots = {}
|
|
77
|
+
root = roots.get(scheme) if isinstance(roots, dict) else None
|
|
78
|
+
if not root:
|
|
79
|
+
raise ValueError(f"Protocol paths are not supported by this helper: {path}")
|
|
80
|
+
relative = unquote(match.group(2).replace("\\", "/"))
|
|
81
|
+
# Mirror the host `path.resolve`/`resolveLocalUrlToPath`: normalize and
|
|
82
|
+
# make absolute WITHOUT realpath'ing symlinks (Path.resolve would turn
|
|
83
|
+
# /tmp into /private/tmp and diverge from the read-side resolution).
|
|
84
|
+
root_path = os.path.abspath(root)
|
|
85
|
+
if relative == "":
|
|
86
|
+
return Path(root_path)
|
|
87
|
+
rel_path = Path(relative)
|
|
88
|
+
if rel_path.is_absolute() or ".." in rel_path.parts:
|
|
89
|
+
raise ValueError(f"Unsafe {scheme}:// path (absolute or traversal): {path}")
|
|
90
|
+
resolved = os.path.abspath(os.path.join(root_path, relative))
|
|
91
|
+
if resolved != root_path and not resolved.startswith(root_path + os.sep):
|
|
92
|
+
raise ValueError(f"{scheme}:// path escapes its root: {path}")
|
|
93
|
+
return Path(resolved)
|
|
94
|
+
|
|
56
95
|
def read(path: str | Path, offset: int = 1, limit: int | None = None) -> str:
|
|
57
96
|
"""Read file contents. offset/limit are 1-indexed line numbers."""
|
|
58
|
-
p =
|
|
97
|
+
p = _resolve_omp_path(path)
|
|
59
98
|
data = p.read_text(encoding="utf-8")
|
|
60
99
|
lines = data.splitlines(keepends=True)
|
|
61
100
|
if offset > 1 or limit is not None:
|
|
@@ -69,7 +108,7 @@ if "__omp_prelude_loaded__" not in globals():
|
|
|
69
108
|
|
|
70
109
|
def write(path: str | Path, content: str) -> Path:
|
|
71
110
|
"""Write file contents (create parents)."""
|
|
72
|
-
p =
|
|
111
|
+
p = _resolve_omp_path(path)
|
|
73
112
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
74
113
|
p.write_text(content, encoding="utf-8")
|
|
75
114
|
_emit_status("write", path=str(p), chars=len(content))
|
|
@@ -77,7 +116,7 @@ if "__omp_prelude_loaded__" not in globals():
|
|
|
77
116
|
|
|
78
117
|
def append(path: str | Path, content: str) -> Path:
|
|
79
118
|
"""Append to file."""
|
|
80
|
-
p =
|
|
119
|
+
p = _resolve_omp_path(path)
|
|
81
120
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
82
121
|
with p.open("a", encoding="utf-8") as f:
|
|
83
122
|
f.write(content)
|
package/src/eval/py/runner.py
CHANGED
package/src/exa/index.ts
CHANGED
|
@@ -1,27 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exa MCP Tools
|
|
3
|
-
*
|
|
4
|
-
* 22 tools for Exa's MCP servers:
|
|
5
|
-
* - 4 search tools (search, deep, code, crawl)
|
|
6
|
-
* - 1 LinkedIn search tool
|
|
7
|
-
* - 1 company research tool
|
|
8
|
-
* - 2 researcher tools (start, poll)
|
|
9
|
-
* - 14 websets tools (CRUD, items, search, enrichment, monitor)
|
|
10
|
-
*/
|
|
11
|
-
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
12
|
-
import { researcherTools } from "./researcher";
|
|
13
|
-
import { searchTools } from "./search";
|
|
14
|
-
import type { ExaRenderDetails } from "./types";
|
|
15
|
-
import { websetsTools } from "./websets";
|
|
16
|
-
|
|
17
|
-
/** All Exa tools (22 total) - static export for backward compatibility */
|
|
18
|
-
export const exaTools: CustomTool<any, ExaRenderDetails>[] = [...searchTools, ...researcherTools, ...websetsTools];
|
|
19
|
-
|
|
20
1
|
export * from "./mcp-client";
|
|
21
|
-
export {
|
|
22
|
-
export { researcherTools } from "./researcher";
|
|
23
|
-
// Re-export individual modules for selective importing
|
|
24
|
-
export { searchTools } from "./search";
|
|
25
|
-
// Re-export types and utilities
|
|
26
|
-
export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult, MCPToolWrapperConfig } from "./types";
|
|
27
|
-
export { websetsTools } from "./websets";
|
|
2
|
+
export type { ExaSearchResponse, MCPCallResponse, MCPTool, MCPToolsResponse, MCPToolWrapperConfig } from "./types";
|
package/src/exa/mcp-client.ts
CHANGED
|
@@ -2,14 +2,14 @@ import type { TSchema } from "@oh-my-pi/pi-ai";
|
|
|
2
2
|
import { $env, logger } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import type { CustomTool, CustomToolResult } from "../extensibility/custom-tools/types";
|
|
4
4
|
import { type CallMcpOptions, callMCP } from "../mcp/json-rpc";
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
5
|
+
import type { ExaSearchResponse, MCPCallResponse, MCPTool, MCPToolsResponse, MCPToolWrapperConfig } from "./types";
|
|
6
|
+
|
|
7
|
+
type MCPWrappedToolDetails = {
|
|
8
|
+
response?: ExaSearchResponse;
|
|
9
|
+
error?: string;
|
|
10
|
+
toolName?: string;
|
|
11
|
+
raw?: unknown;
|
|
12
|
+
};
|
|
13
13
|
|
|
14
14
|
/** Find EXA_API_KEY from Bun.env or .env files */
|
|
15
15
|
export function findApiKey(): string | null {
|
|
@@ -296,7 +296,7 @@ export async function fetchMCPToolSchema(
|
|
|
296
296
|
* This allows tools to be generated from MCP server schemas without hardcoding,
|
|
297
297
|
* reducing drift when MCP servers add new parameters.
|
|
298
298
|
*/
|
|
299
|
-
export class MCPWrappedTool implements CustomTool<TSchema,
|
|
299
|
+
export class MCPWrappedTool implements CustomTool<TSchema, MCPWrappedToolDetails> {
|
|
300
300
|
readonly name: string;
|
|
301
301
|
readonly label: string;
|
|
302
302
|
|
|
@@ -315,7 +315,7 @@ export class MCPWrappedTool implements CustomTool<TSchema, ExaRenderDetails> {
|
|
|
315
315
|
_onUpdate?: unknown,
|
|
316
316
|
_ctx?: unknown,
|
|
317
317
|
_signal?: AbortSignal,
|
|
318
|
-
): Promise<CustomToolResult<
|
|
318
|
+
): Promise<CustomToolResult<MCPWrappedToolDetails>> {
|
|
319
319
|
try {
|
|
320
320
|
const apiKey = findApiKey();
|
|
321
321
|
// Websets tools require an API key; basic Exa MCP tools work without one
|
package/src/exa/types.ts
CHANGED
|
@@ -5,10 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { TSchema } from "@oh-my-pi/pi-ai";
|
|
7
7
|
|
|
8
|
-
/** MCP endpoint URLs */
|
|
9
|
-
export const EXA_MCP_URL = "https://mcp.exa.ai/mcp";
|
|
10
|
-
export const WEBSETS_MCP_URL = "https://websetsmcp.exa.ai/mcp";
|
|
11
|
-
|
|
12
8
|
/** MCP tool definition from server */
|
|
13
9
|
export interface MCPTool {
|
|
14
10
|
name: string;
|
|
@@ -71,96 +67,3 @@ export interface ExaSearchResponse {
|
|
|
71
67
|
searchTime?: number;
|
|
72
68
|
requestId?: string;
|
|
73
69
|
}
|
|
74
|
-
|
|
75
|
-
/** Researcher task status */
|
|
76
|
-
export interface ResearcherStatus {
|
|
77
|
-
id: string;
|
|
78
|
-
status: "pending" | "running" | "completed" | "failed";
|
|
79
|
-
result?: string;
|
|
80
|
-
error?: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Webset definition */
|
|
84
|
-
export interface Webset {
|
|
85
|
-
id: string;
|
|
86
|
-
name: string;
|
|
87
|
-
description?: string;
|
|
88
|
-
createdAt?: string;
|
|
89
|
-
updatedAt?: string;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Webset item */
|
|
93
|
-
export interface WebsetItem {
|
|
94
|
-
id: string;
|
|
95
|
-
websetId: string;
|
|
96
|
-
url: string;
|
|
97
|
-
title?: string;
|
|
98
|
-
content?: string;
|
|
99
|
-
metadata?: Record<string, unknown>;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Webset search */
|
|
103
|
-
export interface WebsetSearch {
|
|
104
|
-
id: string;
|
|
105
|
-
websetId: string;
|
|
106
|
-
query: string;
|
|
107
|
-
status: "pending" | "running" | "completed" | "cancelled";
|
|
108
|
-
resultCount?: number;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Webset enrichment */
|
|
112
|
-
export interface WebsetEnrichment {
|
|
113
|
-
id: string;
|
|
114
|
-
websetId: string;
|
|
115
|
-
name: string;
|
|
116
|
-
prompt: string;
|
|
117
|
-
status: "pending" | "running" | "completed" | "cancelled";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Tool name mappings: MCP name -> our tool name */
|
|
121
|
-
export const EXA_TOOL_MAPPINGS = {
|
|
122
|
-
// Search tools
|
|
123
|
-
web_search_exa: "exa_search",
|
|
124
|
-
get_code_context_exa: "exa_search_code",
|
|
125
|
-
crawling_exa: "exa_crawl",
|
|
126
|
-
// LinkedIn
|
|
127
|
-
linkedin_search_exa: "exa_linkedin",
|
|
128
|
-
// Company
|
|
129
|
-
company_research_exa: "exa_company",
|
|
130
|
-
// Researcher
|
|
131
|
-
deep_researcher_start: "exa_researcher_start",
|
|
132
|
-
deep_researcher_check: "exa_researcher_poll",
|
|
133
|
-
} as const;
|
|
134
|
-
|
|
135
|
-
export const WEBSETS_TOOL_MAPPINGS = {
|
|
136
|
-
create_webset: "webset_create",
|
|
137
|
-
list_websets: "webset_list",
|
|
138
|
-
get_webset: "webset_get",
|
|
139
|
-
update_webset: "webset_update",
|
|
140
|
-
delete_webset: "webset_delete",
|
|
141
|
-
list_webset_items: "webset_items_list",
|
|
142
|
-
get_item: "webset_item_get",
|
|
143
|
-
create_search: "webset_search_create",
|
|
144
|
-
get_search: "webset_search_get",
|
|
145
|
-
cancel_search: "webset_search_cancel",
|
|
146
|
-
create_enrichment: "webset_enrichment_create",
|
|
147
|
-
get_enrichment: "webset_enrichment_get",
|
|
148
|
-
update_enrichment: "webset_enrichment_update",
|
|
149
|
-
delete_enrichment: "webset_enrichment_delete",
|
|
150
|
-
cancel_enrichment: "webset_enrichment_cancel",
|
|
151
|
-
create_monitor: "webset_monitor_create",
|
|
152
|
-
} as const;
|
|
153
|
-
|
|
154
|
-
export type ExaMcpToolName = keyof typeof EXA_TOOL_MAPPINGS;
|
|
155
|
-
export type WebsetsMcpToolName = keyof typeof WEBSETS_TOOL_MAPPINGS;
|
|
156
|
-
export type ExaToolName = (typeof EXA_TOOL_MAPPINGS)[ExaMcpToolName];
|
|
157
|
-
export type WebsetsToolName = (typeof WEBSETS_TOOL_MAPPINGS)[WebsetsMcpToolName];
|
|
158
|
-
|
|
159
|
-
/** Render details for TUI */
|
|
160
|
-
export interface ExaRenderDetails {
|
|
161
|
-
response?: ExaSearchResponse;
|
|
162
|
-
error?: string;
|
|
163
|
-
toolName?: string;
|
|
164
|
-
/** Raw result for non-search responses */
|
|
165
|
-
raw?: unknown;
|
|
166
|
-
}
|