@sammorrowdrums/mcpi-ext 0.2.1 → 1.0.1
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/LICENSE +21 -0
- package/README.md +426 -151
- package/dist/code-mode/eligibility.d.ts +29 -4
- package/dist/code-mode/eligibility.js +56 -6
- package/dist/code-mode/executor.d.ts +19 -1
- package/dist/code-mode/executor.js +73 -8
- package/dist/code-mode/index.d.ts +54 -12
- package/dist/code-mode/index.js +157 -65
- package/dist/code-mode/isolated-vm.d.ts +65 -0
- package/dist/code-mode/isolated-vm.js +116 -0
- package/dist/code-mode/tools.d.ts +4 -1
- package/dist/code-mode/tools.js +7 -4
- package/dist/code-mode/type-hints.d.ts +2 -2
- package/dist/code-mode/type-hints.js +24 -8
- package/dist/docker-e2e.d.ts +0 -1
- package/dist/docker-e2e.js +0 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +146 -67
- package/dist/mcp/call-tool-result.d.ts +10 -0
- package/dist/mcp/call-tool-result.js +70 -0
- package/dist/mcp/client-factory.d.ts +59 -0
- package/dist/mcp/client-factory.js +76 -0
- package/dist/mcp/client-manager.d.ts +61 -19
- package/dist/mcp/client-manager.js +223 -64
- package/dist/mcp/config-loader.d.ts +0 -1
- package/dist/mcp/config-loader.js +0 -1
- package/dist/mcp/config.d.ts +11 -1
- package/dist/mcp/config.js +15 -1
- package/dist/mcp/gateway-defaults.d.ts +14 -0
- package/dist/mcp/gateway-defaults.js +21 -0
- package/dist/mcp/host-approval.d.ts +16 -0
- package/dist/mcp/host-approval.js +19 -0
- package/dist/mcp/host-elicitation.d.ts +19 -0
- package/dist/mcp/host-elicitation.js +94 -0
- package/dist/mcp/index.d.ts +4 -1
- package/dist/mcp/index.js +4 -1
- package/dist/mcp/policy.d.ts +314 -0
- package/dist/mcp/policy.js +848 -0
- package/dist/routing/facilities.d.ts +109 -0
- package/dist/routing/facilities.js +175 -0
- package/dist/routing/format.d.ts +24 -0
- package/dist/routing/format.js +64 -0
- package/dist/routing/index.d.ts +4 -0
- package/dist/routing/index.js +4 -0
- package/dist/routing/seam.d.ts +35 -0
- package/dist/routing/seam.js +28 -0
- package/dist/routing/tripwire.d.ts +42 -0
- package/dist/routing/tripwire.js +80 -0
- package/dist/skills/discover.d.ts +5 -3
- package/dist/skills/discover.js +37 -12
- package/dist/skills/format.d.ts +4 -1
- package/dist/skills/format.js +8 -4
- package/dist/skills/index.d.ts +2 -2
- package/dist/skills/index.js +1 -1
- package/dist/skills/load-skill-tool.d.ts +23 -7
- package/dist/skills/load-skill-tool.js +112 -33
- package/dist/skills/mcp-tool-proxy.d.ts +4 -8
- package/dist/skills/mcp-tool-proxy.js +22 -97
- package/dist/skills/sep2640/client.d.ts +91 -0
- package/dist/skills/sep2640/client.js +160 -0
- package/dist/skills/sep2640/discover.d.ts +33 -0
- package/dist/skills/sep2640/discover.js +82 -0
- package/dist/skills/sep2640/index.d.ts +13 -0
- package/dist/skills/sep2640/index.js +13 -0
- package/dist/skills/sep2640/integrity.d.ts +58 -0
- package/dist/skills/sep2640/integrity.js +185 -0
- package/dist/skills/sep2640/load.d.ts +61 -0
- package/dist/skills/sep2640/load.js +90 -0
- package/dist/skills/sep2640/protocol.d.ts +146 -0
- package/dist/skills/sep2640/protocol.js +214 -0
- package/dist/skills/sep2640/spec.d.ts +76 -0
- package/dist/skills/sep2640/spec.js +88 -0
- package/dist/skills/skill-registry.d.ts +48 -2
- package/dist/skills/skill-registry.js +40 -3
- package/dist/tool-cli/bridge.d.ts +14 -0
- package/dist/tool-cli/bridge.js +196 -0
- package/dist/tool-cli/format.d.ts +17 -5
- package/dist/tool-cli/format.js +36 -20
- package/dist/tool-cli/index.d.ts +5 -2
- package/dist/tool-cli/index.js +3 -1
- package/dist/tool-cli/provider.d.ts +21 -0
- package/dist/tool-cli/provider.js +114 -0
- package/dist/tool-cli/startup.d.ts +22 -0
- package/dist/tool-cli/startup.js +47 -0
- package/package.json +78 -30
- package/dist/code-mode/code-mode-integration.test.d.ts +0 -2
- package/dist/code-mode/code-mode-integration.test.d.ts.map +0 -1
- package/dist/code-mode/code-mode-integration.test.js +0 -116
- package/dist/code-mode/code-mode-integration.test.js.map +0 -1
- package/dist/code-mode/eligibility.d.ts.map +0 -1
- package/dist/code-mode/eligibility.js.map +0 -1
- package/dist/code-mode/eligibility.test.d.ts +0 -2
- package/dist/code-mode/eligibility.test.d.ts.map +0 -1
- package/dist/code-mode/eligibility.test.js +0 -53
- package/dist/code-mode/eligibility.test.js.map +0 -1
- package/dist/code-mode/executor.d.ts.map +0 -1
- package/dist/code-mode/executor.js.map +0 -1
- package/dist/code-mode/executor.test.d.ts +0 -2
- package/dist/code-mode/executor.test.d.ts.map +0 -1
- package/dist/code-mode/executor.test.js +0 -130
- package/dist/code-mode/executor.test.js.map +0 -1
- package/dist/code-mode/index.d.ts.map +0 -1
- package/dist/code-mode/index.js.map +0 -1
- package/dist/code-mode/tools.d.ts.map +0 -1
- package/dist/code-mode/tools.js.map +0 -1
- package/dist/code-mode/type-hints.d.ts.map +0 -1
- package/dist/code-mode/type-hints.js.map +0 -1
- package/dist/code-mode/type-hints.test.d.ts +0 -2
- package/dist/code-mode/type-hints.test.d.ts.map +0 -1
- package/dist/code-mode/type-hints.test.js +0 -157
- package/dist/code-mode/type-hints.test.js.map +0 -1
- package/dist/docker-e2e.d.ts.map +0 -1
- package/dist/docker-e2e.js.map +0 -1
- package/dist/docker-e2e.test.d.ts +0 -2
- package/dist/docker-e2e.test.d.ts.map +0 -1
- package/dist/docker-e2e.test.js +0 -176
- package/dist/docker-e2e.test.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -8
- package/dist/index.test.js.map +0 -1
- package/dist/mcp/client-manager.d.ts.map +0 -1
- package/dist/mcp/client-manager.js.map +0 -1
- package/dist/mcp/client-manager.test.d.ts +0 -2
- package/dist/mcp/client-manager.test.d.ts.map +0 -1
- package/dist/mcp/client-manager.test.js +0 -204
- package/dist/mcp/client-manager.test.js.map +0 -1
- package/dist/mcp/config-loader.d.ts.map +0 -1
- package/dist/mcp/config-loader.js.map +0 -1
- package/dist/mcp/config-loader.test.d.ts +0 -2
- package/dist/mcp/config-loader.test.d.ts.map +0 -1
- package/dist/mcp/config-loader.test.js +0 -93
- package/dist/mcp/config-loader.test.js.map +0 -1
- package/dist/mcp/config.d.ts.map +0 -1
- package/dist/mcp/config.js.map +0 -1
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/index.js.map +0 -1
- package/dist/skills/discover.d.ts.map +0 -1
- package/dist/skills/discover.js.map +0 -1
- package/dist/skills/format.d.ts.map +0 -1
- package/dist/skills/format.js.map +0 -1
- package/dist/skills/format.test.d.ts +0 -2
- package/dist/skills/format.test.d.ts.map +0 -1
- package/dist/skills/format.test.js +0 -63
- package/dist/skills/format.test.js.map +0 -1
- package/dist/skills/index.d.ts.map +0 -1
- package/dist/skills/index.js.map +0 -1
- package/dist/skills/load-skill-tool.d.ts.map +0 -1
- package/dist/skills/load-skill-tool.js.map +0 -1
- package/dist/skills/mcp-tool-proxy.d.ts.map +0 -1
- package/dist/skills/mcp-tool-proxy.js.map +0 -1
- package/dist/skills/skill-integration.test.d.ts +0 -2
- package/dist/skills/skill-integration.test.d.ts.map +0 -1
- package/dist/skills/skill-integration.test.js +0 -119
- package/dist/skills/skill-integration.test.js.map +0 -1
- package/dist/skills/skill-registry.d.ts.map +0 -1
- package/dist/skills/skill-registry.js.map +0 -1
- package/dist/skills/skill-registry.test.d.ts +0 -2
- package/dist/skills/skill-registry.test.d.ts.map +0 -1
- package/dist/skills/skill-registry.test.js +0 -67
- package/dist/skills/skill-registry.test.js.map +0 -1
- package/dist/test-servers/weather-server.d.ts +0 -12
- package/dist/test-servers/weather-server.d.ts.map +0 -1
- package/dist/test-servers/weather-server.js +0 -139
- package/dist/test-servers/weather-server.js.map +0 -1
- package/dist/test-servers/weather-stdio.d.ts +0 -2
- package/dist/test-servers/weather-stdio.d.ts.map +0 -1
- package/dist/test-servers/weather-stdio.js +0 -11
- package/dist/test-servers/weather-stdio.js.map +0 -1
- package/dist/tool-cli/cli.d.ts +0 -3
- package/dist/tool-cli/cli.d.ts.map +0 -1
- package/dist/tool-cli/cli.js +0 -179
- package/dist/tool-cli/cli.js.map +0 -1
- package/dist/tool-cli/constants.d.ts +0 -7
- package/dist/tool-cli/constants.d.ts.map +0 -1
- package/dist/tool-cli/constants.js +0 -15
- package/dist/tool-cli/constants.js.map +0 -1
- package/dist/tool-cli/format.d.ts.map +0 -1
- package/dist/tool-cli/format.js.map +0 -1
- package/dist/tool-cli/format.test.d.ts +0 -2
- package/dist/tool-cli/format.test.d.ts.map +0 -1
- package/dist/tool-cli/format.test.js +0 -30
- package/dist/tool-cli/format.test.js.map +0 -1
- package/dist/tool-cli/index.d.ts.map +0 -1
- package/dist/tool-cli/index.js.map +0 -1
- package/dist/tool-cli/rpc-client.d.ts +0 -6
- package/dist/tool-cli/rpc-client.d.ts.map +0 -1
- package/dist/tool-cli/rpc-client.js +0 -25
- package/dist/tool-cli/rpc-client.js.map +0 -1
- package/dist/tool-cli/rpc-server.d.ts +0 -48
- package/dist/tool-cli/rpc-server.d.ts.map +0 -1
- package/dist/tool-cli/rpc-server.js +0 -220
- package/dist/tool-cli/rpc-server.js.map +0 -1
- package/dist/tool-cli/rpc-server.test.d.ts +0 -2
- package/dist/tool-cli/rpc-server.test.d.ts.map +0 -1
- package/dist/tool-cli/rpc-server.test.js +0 -154
- package/dist/tool-cli/rpc-server.test.js.map +0 -1
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
import type { McpClientManager, McpTool } from "../mcp/index.js";
|
|
2
|
+
export declare const SYNTHESIZED_OUTPUT_SCHEMA: NonNullable<McpTool["outputSchema"]>;
|
|
3
|
+
export type OutputSchemaProvenance = "declared" | "synthesized" | "unavailable";
|
|
4
|
+
export type CodeModeRefusalReason = "destructive_hint" | "read_only_hint_required";
|
|
2
5
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
+
* Client-internal Code Mode metadata. The source MCP tool is retained separately
|
|
7
|
+
* so provenance never becomes part of an MCP request or response.
|
|
8
|
+
*/
|
|
9
|
+
export interface CodeModeTool {
|
|
10
|
+
readonly tool: McpTool;
|
|
11
|
+
readonly callable: boolean;
|
|
12
|
+
readonly refusalReasons: readonly CodeModeRefusalReason[];
|
|
13
|
+
readonly outputSchema?: NonNullable<McpTool["outputSchema"]>;
|
|
14
|
+
readonly outputSchemaProvenance: OutputSchemaProvenance;
|
|
15
|
+
}
|
|
16
|
+
export interface CodeModeDiagnostics {
|
|
17
|
+
readonly totalTools: number;
|
|
18
|
+
readonly callableTools: number;
|
|
19
|
+
readonly refusedTools: number;
|
|
20
|
+
readonly declaredOutputSchemas: number;
|
|
21
|
+
readonly synthesizedOutputSchemas: number;
|
|
22
|
+
readonly unavailableOutputSchemas: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Code Mode dispatch is allowed only when a tool explicitly declares itself
|
|
26
|
+
* read-only and does not also declare destructive behavior.
|
|
6
27
|
*/
|
|
7
28
|
export declare function isEligibleForCodeMode(tool: McpTool): boolean;
|
|
8
29
|
/** Get all tools eligible for code mode across all connected MCP servers. */
|
|
9
30
|
export declare function getEligibleTools(mcpManager: McpClientManager): McpTool[];
|
|
10
|
-
|
|
31
|
+
/** Build the internal Code Mode catalog without mutating source MCP tool definitions. */
|
|
32
|
+
export declare function toCodeModeTool(tool: McpTool): CodeModeTool;
|
|
33
|
+
/** Get every discovered MCP tool with Code Mode permission and schema metadata. */
|
|
34
|
+
export declare function getCodeModeTools(mcpManager: McpClientManager): CodeModeTool[];
|
|
35
|
+
export declare function getCodeModeDiagnostics(tools: readonly CodeModeTool[]): CodeModeDiagnostics;
|
|
@@ -1,14 +1,64 @@
|
|
|
1
|
+
export const SYNTHESIZED_OUTPUT_SCHEMA = Object.freeze({});
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* 2. outputSchema is defined — results are typed and parseable
|
|
3
|
+
* Code Mode dispatch is allowed only when a tool explicitly declares itself
|
|
4
|
+
* read-only and does not also declare destructive behavior.
|
|
5
5
|
*/
|
|
6
6
|
export function isEligibleForCodeMode(tool) {
|
|
7
|
-
|
|
8
|
-
return annotations?.readOnlyHint === true && tool.outputSchema != null;
|
|
7
|
+
return tool.annotations?.readOnlyHint === true && tool.annotations.destructiveHint !== true;
|
|
9
8
|
}
|
|
10
9
|
/** Get all tools eligible for code mode across all connected MCP servers. */
|
|
11
10
|
export function getEligibleTools(mcpManager) {
|
|
12
11
|
return mcpManager.getTools().filter(isEligibleForCodeMode);
|
|
13
12
|
}
|
|
14
|
-
|
|
13
|
+
/** Build the internal Code Mode catalog without mutating source MCP tool definitions. */
|
|
14
|
+
export function toCodeModeTool(tool) {
|
|
15
|
+
const refusalReasons = [];
|
|
16
|
+
if (tool.annotations?.readOnlyHint !== true) {
|
|
17
|
+
refusalReasons.push("read_only_hint_required");
|
|
18
|
+
}
|
|
19
|
+
if (tool.annotations?.destructiveHint === true) {
|
|
20
|
+
refusalReasons.push("destructive_hint");
|
|
21
|
+
}
|
|
22
|
+
const callable = refusalReasons.length === 0;
|
|
23
|
+
const declaredOutputSchema = tool.outputSchema !== undefined && tool.outputSchema !== null ? tool.outputSchema : undefined;
|
|
24
|
+
if (declaredOutputSchema) {
|
|
25
|
+
return {
|
|
26
|
+
tool,
|
|
27
|
+
callable,
|
|
28
|
+
refusalReasons,
|
|
29
|
+
outputSchema: declaredOutputSchema,
|
|
30
|
+
outputSchemaProvenance: "declared",
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (callable) {
|
|
34
|
+
return {
|
|
35
|
+
tool,
|
|
36
|
+
callable,
|
|
37
|
+
refusalReasons,
|
|
38
|
+
outputSchema: SYNTHESIZED_OUTPUT_SCHEMA,
|
|
39
|
+
outputSchemaProvenance: "synthesized",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
tool,
|
|
44
|
+
callable,
|
|
45
|
+
refusalReasons,
|
|
46
|
+
outputSchemaProvenance: "unavailable",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Get every discovered MCP tool with Code Mode permission and schema metadata. */
|
|
50
|
+
export function getCodeModeTools(mcpManager) {
|
|
51
|
+
return mcpManager.getTools().map(toCodeModeTool);
|
|
52
|
+
}
|
|
53
|
+
export function getCodeModeDiagnostics(tools) {
|
|
54
|
+
const callableTools = tools.filter((tool) => tool.callable).length;
|
|
55
|
+
const countProvenance = (provenance) => tools.filter((tool) => tool.outputSchemaProvenance === provenance).length;
|
|
56
|
+
return {
|
|
57
|
+
totalTools: tools.length,
|
|
58
|
+
callableTools,
|
|
59
|
+
refusedTools: tools.length - callableTools,
|
|
60
|
+
declaredOutputSchemas: countProvenance("declared"),
|
|
61
|
+
synthesizedOutputSchemas: countProvenance("synthesized"),
|
|
62
|
+
unavailableOutputSchemas: countProvenance("unavailable"),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -2,8 +2,20 @@
|
|
|
2
2
|
export interface ExecuteResult {
|
|
3
3
|
result: unknown;
|
|
4
4
|
error?: string;
|
|
5
|
+
errorDetails?: CodeModeErrorDetails;
|
|
5
6
|
logs: string[];
|
|
6
7
|
}
|
|
8
|
+
export interface CodeModeErrorDetails {
|
|
9
|
+
error: string;
|
|
10
|
+
message: string;
|
|
11
|
+
alternatives?: string[];
|
|
12
|
+
toolName?: string;
|
|
13
|
+
reason?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class CodeModeDispatchError extends Error {
|
|
16
|
+
readonly details: CodeModeErrorDetails;
|
|
17
|
+
constructor(details: CodeModeErrorDetails);
|
|
18
|
+
}
|
|
7
19
|
/** A function the sandbox can call to invoke an MCP tool. */
|
|
8
20
|
export type ToolDispatchFn = (toolName: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
9
21
|
export interface ExecutorOptions {
|
|
@@ -12,6 +24,8 @@ export interface ExecutorOptions {
|
|
|
12
24
|
/** Execution timeout in milliseconds. Default: 30000. */
|
|
13
25
|
timeoutMs?: number;
|
|
14
26
|
}
|
|
27
|
+
/** Error code surfaced when the V8 isolate backend is not installed. */
|
|
28
|
+
export declare const SANDBOX_UNAVAILABLE_ERROR = "sandbox_unavailable";
|
|
15
29
|
/**
|
|
16
30
|
* Execute model-generated JavaScript code in an isolated V8 sandbox.
|
|
17
31
|
*
|
|
@@ -23,6 +37,11 @@ export interface ExecutorOptions {
|
|
|
23
37
|
*
|
|
24
38
|
* Tool calls are dispatched to the host via `Reference` callbacks —
|
|
25
39
|
* actual MCP tool execution happens outside the sandbox.
|
|
40
|
+
*
|
|
41
|
+
* The `isolated-vm` addon is optional and loaded lazily. If it is unavailable
|
|
42
|
+
* this returns a structured `sandbox_unavailable` error rather than falling
|
|
43
|
+
* back to Node's `vm` module: `node:vm` shares the host realm and heap, so
|
|
44
|
+
* using it here would silently void the isolation guarantee this API makes.
|
|
26
45
|
*/
|
|
27
46
|
export declare function executeInSandbox(code: string, toolNames: string[], dispatch: ToolDispatchFn, options?: ExecutorOptions): Promise<ExecuteResult>;
|
|
28
47
|
/**
|
|
@@ -32,4 +51,3 @@ export declare function executeInSandbox(code: string, toolNames: string[], disp
|
|
|
32
51
|
* - Otherwise leave code as a statement block; callers must explicitly `return` a value
|
|
33
52
|
*/
|
|
34
53
|
export declare function normalizeCode(code: string): string;
|
|
35
|
-
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { loadIsolatedVm } from "./isolated-vm.js";
|
|
2
2
|
import { sanitizeToolName } from "./type-hints.js";
|
|
3
|
+
export class CodeModeDispatchError extends Error {
|
|
4
|
+
details;
|
|
5
|
+
constructor(details) {
|
|
6
|
+
super(details.message);
|
|
7
|
+
this.details = details;
|
|
8
|
+
this.name = "CodeModeDispatchError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
3
11
|
const DEFAULT_MEMORY_LIMIT = 128;
|
|
4
12
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
13
|
+
const STRUCTURED_ERROR_PREFIX = "__CODE_MODE_ERROR__";
|
|
14
|
+
/** Error code surfaced when the V8 isolate backend is not installed. */
|
|
15
|
+
export const SANDBOX_UNAVAILABLE_ERROR = "sandbox_unavailable";
|
|
5
16
|
/**
|
|
6
17
|
* Execute model-generated JavaScript code in an isolated V8 sandbox.
|
|
7
18
|
*
|
|
@@ -13,19 +24,40 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
13
24
|
*
|
|
14
25
|
* Tool calls are dispatched to the host via `Reference` callbacks —
|
|
15
26
|
* actual MCP tool execution happens outside the sandbox.
|
|
27
|
+
*
|
|
28
|
+
* The `isolated-vm` addon is optional and loaded lazily. If it is unavailable
|
|
29
|
+
* this returns a structured `sandbox_unavailable` error rather than falling
|
|
30
|
+
* back to Node's `vm` module: `node:vm` shares the host realm and heap, so
|
|
31
|
+
* using it here would silently void the isolation guarantee this API makes.
|
|
16
32
|
*/
|
|
17
33
|
export async function executeInSandbox(code, toolNames, dispatch, options = {}) {
|
|
18
34
|
const memoryLimit = options.memoryLimit ?? DEFAULT_MEMORY_LIMIT;
|
|
19
35
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
36
|
+
const load = await loadIsolatedVm();
|
|
37
|
+
if (!load.available) {
|
|
38
|
+
const message = `Code Mode is unavailable: ${load.reason}. Use tool-cli or the MCP tool proxies instead.`;
|
|
39
|
+
return {
|
|
40
|
+
result: undefined,
|
|
41
|
+
error: message,
|
|
42
|
+
errorDetails: {
|
|
43
|
+
error: SANDBOX_UNAVAILABLE_ERROR,
|
|
44
|
+
message,
|
|
45
|
+
reason: load.reason,
|
|
46
|
+
alternatives: ["tool-cli", "MCP tool proxies"],
|
|
47
|
+
},
|
|
48
|
+
logs: [],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const ivm = load.module;
|
|
20
52
|
const isolate = new ivm.Isolate({ memoryLimit });
|
|
21
53
|
try {
|
|
22
|
-
return await runInIsolate(isolate, code, toolNames, dispatch, timeoutMs);
|
|
54
|
+
return await runInIsolate(ivm, isolate, code, toolNames, dispatch, timeoutMs);
|
|
23
55
|
}
|
|
24
56
|
finally {
|
|
25
57
|
isolate.dispose();
|
|
26
58
|
}
|
|
27
59
|
}
|
|
28
|
-
async function runInIsolate(isolate, code, toolNames, dispatch, timeoutMs) {
|
|
60
|
+
async function runInIsolate(ivm, isolate, code, toolNames, dispatch, timeoutMs) {
|
|
29
61
|
const ctx = await isolate.createContext();
|
|
30
62
|
const jail = ctx.global;
|
|
31
63
|
const logs = [];
|
|
@@ -37,8 +69,16 @@ async function runInIsolate(isolate, code, toolNames, dispatch, timeoutMs) {
|
|
|
37
69
|
// Inject tool dispatcher Reference (async callback)
|
|
38
70
|
const dispatchRef = new ivm.Reference(async (toolName, argsJson) => {
|
|
39
71
|
const args = JSON.parse(argsJson);
|
|
40
|
-
|
|
41
|
-
|
|
72
|
+
try {
|
|
73
|
+
const result = await dispatch(toolName, args);
|
|
74
|
+
return JSON.stringify({ ok: true, value: result === undefined ? null : result });
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error instanceof CodeModeDispatchError) {
|
|
78
|
+
return JSON.stringify({ ok: false, error: error.details });
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
42
82
|
});
|
|
43
83
|
await jail.set("__dispatch", dispatchRef);
|
|
44
84
|
// Build the tool call helper and codemode proxy as setup code
|
|
@@ -57,7 +97,11 @@ async function runInIsolate(isolate, code, toolNames, dispatch, timeoutMs) {
|
|
|
57
97
|
const console = { log: (...args) => __log(args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ')) };
|
|
58
98
|
const __callTool = async (name, args) => {
|
|
59
99
|
const r = await __dispatch.apply(undefined, [name, JSON.stringify(args ?? {})], { arguments: { copy: true }, result: { promise: true, copy: true } });
|
|
60
|
-
|
|
100
|
+
const response = JSON.parse(r);
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(${JSON.stringify(STRUCTURED_ERROR_PREFIX)} + JSON.stringify(response.error));
|
|
103
|
+
}
|
|
104
|
+
return response.value;
|
|
61
105
|
};
|
|
62
106
|
const codemode = {
|
|
63
107
|
listTools: async () => ${JSON.stringify(toolNames)},
|
|
@@ -83,7 +127,13 @@ ${toolProxyEntries}
|
|
|
83
127
|
}
|
|
84
128
|
catch (err) {
|
|
85
129
|
const message = err instanceof Error ? err.message : String(err);
|
|
86
|
-
|
|
130
|
+
const errorDetails = parseStructuredError(message);
|
|
131
|
+
return {
|
|
132
|
+
result: undefined,
|
|
133
|
+
error: errorDetails?.message ?? message,
|
|
134
|
+
errorDetails,
|
|
135
|
+
logs,
|
|
136
|
+
};
|
|
87
137
|
}
|
|
88
138
|
}
|
|
89
139
|
/**
|
|
@@ -117,4 +167,19 @@ export function normalizeCode(code) {
|
|
|
117
167
|
// Otherwise, treat as a code block — return the last expression
|
|
118
168
|
return normalized;
|
|
119
169
|
}
|
|
120
|
-
|
|
170
|
+
function parseStructuredError(message) {
|
|
171
|
+
const markerIndex = message.indexOf(STRUCTURED_ERROR_PREFIX);
|
|
172
|
+
if (markerIndex === -1)
|
|
173
|
+
return undefined;
|
|
174
|
+
const serialized = message.slice(markerIndex + STRUCTURED_ERROR_PREFIX.length);
|
|
175
|
+
try {
|
|
176
|
+
const details = JSON.parse(serialized);
|
|
177
|
+
if (typeof details.error !== "string" || typeof details.message !== "string") {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
return details;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -1,34 +1,77 @@
|
|
|
1
1
|
import type { McpClientManager, McpTool } from "../mcp/index.js";
|
|
2
|
+
import { type McpPolicy } from "../mcp/policy.js";
|
|
3
|
+
import { type CodeModeDiagnostics, type CodeModeTool } from "./eligibility.js";
|
|
2
4
|
import type { ExecuteResult, ExecutorOptions } from "./executor.js";
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
export {
|
|
5
|
+
import { executeInSandbox } from "./executor.js";
|
|
6
|
+
export { SYNTHESIZED_OUTPUT_SCHEMA, getCodeModeDiagnostics, getCodeModeTools, getEligibleTools, isEligibleForCodeMode, toCodeModeTool, } from "./eligibility.js";
|
|
7
|
+
export type { CodeModeDiagnostics, CodeModeRefusalReason, CodeModeTool, OutputSchemaProvenance, } from "./eligibility.js";
|
|
8
|
+
export type { CodeModeErrorDetails, ExecuteResult, ExecutorOptions } from "./executor.js";
|
|
9
|
+
export { SANDBOX_UNAVAILABLE_ERROR, executeInSandbox, normalizeCode } from "./executor.js";
|
|
10
|
+
export { loadIsolatedVm, peekIsolatedVm, resetIsolatedVmCacheForTests, setIsolatedVmForTests, type IsolatedVmLoad, type IsolatedVmModule, } from "./isolated-vm.js";
|
|
6
11
|
export { createCodeExecuteTool, createCodeSearchTool } from "./tools.js";
|
|
7
12
|
export { generateTypeHints, jsonSchemaToTypeString, sanitizeToolName } from "./type-hints.js";
|
|
8
13
|
export interface CodeModeManagerOptions extends ExecutorOptions {
|
|
9
14
|
/** Log function for status messages. */
|
|
10
15
|
log?: (msg: string) => void;
|
|
16
|
+
/** Test seam for proving pre-isolate refusals. */
|
|
17
|
+
sandboxExecutor?: typeof executeInSandbox;
|
|
11
18
|
}
|
|
12
19
|
/**
|
|
13
|
-
*
|
|
20
|
+
* Whether the sandbox backend can run code.
|
|
21
|
+
*
|
|
22
|
+
* `unknown` is a real state, not a synonym for unavailable: before the optional
|
|
23
|
+
* native addon has been probed we have not established anything, and reporting
|
|
24
|
+
* that honestly is better than guessing in either direction.
|
|
25
|
+
*/
|
|
26
|
+
export interface SandboxAvailability {
|
|
27
|
+
readonly state: "available" | "unavailable" | "unknown";
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Orchestrates code mode: catalogs tools, generates type hints,
|
|
14
32
|
* and executes model-generated code in a sandbox with tool dispatch.
|
|
15
33
|
*/
|
|
16
34
|
export declare class CodeModeManager {
|
|
17
35
|
private mcpManager;
|
|
18
|
-
private
|
|
36
|
+
private policy;
|
|
37
|
+
private codeModeTools;
|
|
38
|
+
private diagnostics;
|
|
19
39
|
private typeHints;
|
|
20
|
-
private options;
|
|
40
|
+
private readonly options;
|
|
41
|
+
private readonly sandboxExecutor;
|
|
42
|
+
private log;
|
|
43
|
+
private lastDiagnosticSummary;
|
|
44
|
+
private sandbox;
|
|
45
|
+
private sandboxProbe;
|
|
21
46
|
constructor(options?: CodeModeManagerOptions);
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Whether code mode should be advertised to the model.
|
|
49
|
+
*
|
|
50
|
+
* Only a *proven* unavailable sandbox switches this off. An unprobed backend
|
|
51
|
+
* stays active because `code_execute` is registered synchronously at load and
|
|
52
|
+
* returns a structured `sandbox_unavailable` error if it turns out it cannot
|
|
53
|
+
* run — a truthful refusal at call time beats hiding a facility that works.
|
|
54
|
+
*/
|
|
55
|
+
get isActive(): boolean;
|
|
56
|
+
/** Current sandbox backend availability, without triggering a probe. */
|
|
57
|
+
getSandboxAvailability(): SandboxAvailability;
|
|
58
|
+
/**
|
|
59
|
+
* Load the optional native addon once and cache the verdict.
|
|
60
|
+
*
|
|
61
|
+
* Safe to call from any lifecycle hook; concurrent callers share one probe.
|
|
62
|
+
*/
|
|
63
|
+
probeSandbox(): Promise<SandboxAvailability>;
|
|
64
|
+
/** Initialize with MCP manager and policy, catalog tools, and generate type hints. */
|
|
65
|
+
initialize(mcpManager: McpClientManager, policy: McpPolicy, log?: (msg: string) => void): void;
|
|
66
|
+
/** Refresh the complete tool catalog and type hints (call on tools/list_changed). */
|
|
25
67
|
refresh(): void;
|
|
26
68
|
/** Get the type hints string for injection into system prompt. */
|
|
27
69
|
getTypeHints(): string;
|
|
28
70
|
/** Get eligible tools. */
|
|
29
71
|
getEligibleTools(): McpTool[];
|
|
30
|
-
/**
|
|
31
|
-
|
|
72
|
+
/** Get the complete client-internal catalog, including permission and schema provenance. */
|
|
73
|
+
getCatalogTools(): readonly CodeModeTool[];
|
|
74
|
+
getDiagnostics(): CodeModeDiagnostics;
|
|
32
75
|
/** Execute code in search mode (tool catalog queries). */
|
|
33
76
|
searchTools(code: string): Promise<ExecuteResult>;
|
|
34
77
|
/** Execute code that chains MCP tool calls. */
|
|
@@ -62,4 +105,3 @@ export declare class CodeModeManager {
|
|
|
62
105
|
formatSystemPromptSection(): string;
|
|
63
106
|
private execute;
|
|
64
107
|
}
|
|
65
|
-
//# sourceMappingURL=index.d.ts.map
|