mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-16 15:43:57 -06:00
7e0b2a2e41
* test(agents): reduce redundant code mode work * test(agents): retain representative Code Mode execution --------- Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
526 lines
18 KiB
TypeScript
526 lines
18 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
enforceOutputLimit,
|
|
enforceResultLimit,
|
|
isCodeModeEngagedForModel,
|
|
prepareSource,
|
|
resolveCodeModeConfig,
|
|
} from "./code-mode-runtime.js";
|
|
import { parseCodeModeScriptSyntax } from "./code-mode-script-syntax.js";
|
|
|
|
const config = resolveCodeModeConfig({ tools: { codeMode: true } } as never);
|
|
|
|
describe("Code Mode output accounting", () => {
|
|
it("accepts Unicode output at its exact serialized byte limit", () => {
|
|
const output = [{ type: "text", text: "😀 café" }];
|
|
const maxOutputBytes = Buffer.byteLength(JSON.stringify(output), "utf8");
|
|
|
|
expect(() => enforceOutputLimit(output, { ...config, maxOutputBytes })).not.toThrow();
|
|
expect(() =>
|
|
enforceOutputLimit(output, { ...config, maxOutputBytes: maxOutputBytes - 1 }),
|
|
).toThrow("code mode output limit exceeded");
|
|
});
|
|
|
|
it("counts serialized output only once against the returned value", () => {
|
|
const output = [{ type: "text", text: "😀" }];
|
|
const value = { result: "café" };
|
|
const maxOutputBytes =
|
|
Buffer.byteLength(JSON.stringify(output), "utf8") +
|
|
Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
|
|
expect(() =>
|
|
enforceResultLimit({ output, value, config: { ...config, maxOutputBytes } }),
|
|
).not.toThrow();
|
|
expect(() =>
|
|
enforceResultLimit({
|
|
output,
|
|
value,
|
|
config: { ...config, maxOutputBytes: maxOutputBytes - 1 },
|
|
}),
|
|
).toThrow("code mode output limit exceeded");
|
|
});
|
|
|
|
it("does not charge an empty output array against the returned value", () => {
|
|
const value = "ok";
|
|
const maxOutputBytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
|
|
expect(() =>
|
|
enforceResultLimit({ output: [], value, config: { ...config, maxOutputBytes } }),
|
|
).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe("Code Mode master switch resolution", () => {
|
|
it.each([
|
|
{ name: "boolean shorthand true", codeMode: true, enabled: true },
|
|
{ name: "boolean shorthand false", codeMode: false, enabled: false },
|
|
{ name: "auto shorthand", codeMode: "auto", enabled: "auto" },
|
|
{ name: "object enabled auto", codeMode: { enabled: "auto" }, enabled: "auto" },
|
|
{ name: "object without enabled", codeMode: { timeoutMs: 5000 }, enabled: "auto" },
|
|
{ name: "omitted", codeMode: undefined, enabled: "auto" },
|
|
])("resolves enabled for $name", ({ codeMode, enabled }) => {
|
|
expect(resolveCodeModeConfig({ tools: { codeMode } } as never).enabled).toBe(enabled);
|
|
});
|
|
|
|
const preferredModel = { compat: { codeMode: "preferred" } };
|
|
const capableModel = { compat: { codeMode: "capable" } };
|
|
const unflaggedModel = { compat: { supportsTools: true } };
|
|
|
|
it.each([
|
|
{
|
|
name: "true engages an unflagged model",
|
|
enabled: true,
|
|
model: unflaggedModel,
|
|
engaged: true,
|
|
},
|
|
{
|
|
name: "false stays off for a preferred model",
|
|
enabled: false,
|
|
model: preferredModel,
|
|
engaged: false,
|
|
},
|
|
{
|
|
name: "auto engages a preferred model",
|
|
enabled: "auto",
|
|
model: preferredModel,
|
|
engaged: true,
|
|
},
|
|
{
|
|
name: "auto skips an explicit capable model",
|
|
enabled: "auto",
|
|
model: capableModel,
|
|
engaged: false,
|
|
},
|
|
{
|
|
name: "auto skips an unflagged model",
|
|
enabled: "auto",
|
|
model: unflaggedModel,
|
|
engaged: false,
|
|
},
|
|
{ name: "auto skips a compat-free model", enabled: "auto", model: {}, engaged: false },
|
|
{ name: "auto skips a missing model", enabled: "auto", model: undefined, engaged: false },
|
|
] as const)("$name", ({ enabled, model, engaged }) => {
|
|
expect(isCodeModeEngagedForModel({ enabled }, model)).toBe(engaged);
|
|
});
|
|
});
|
|
|
|
describe("Code Mode guest source validation", () => {
|
|
it("reports syntax errors at user-relative locations", () => {
|
|
expect(parseCodeModeScriptSyntax("const x = ;")).toEqual({
|
|
ok: false,
|
|
message: "Unexpected token",
|
|
line: 1,
|
|
column: 10,
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: "import-shaped template text",
|
|
code: "return `import('node:fs')`;",
|
|
},
|
|
{
|
|
name: "require-shaped template text",
|
|
code: "return `require('node:fs')`;",
|
|
},
|
|
{
|
|
name: "import.meta-shaped template text",
|
|
code: "return `import.meta.url`;",
|
|
},
|
|
{
|
|
name: "escaped template interpolation",
|
|
code: "return `\\${import('node:fs')}`;",
|
|
},
|
|
{
|
|
name: "escaped template delimiter",
|
|
code: "return `escaped \\` require('node:fs')`;",
|
|
},
|
|
{
|
|
name: "astral Unicode before harmless template text",
|
|
code: "const emoji = '😀'; return `import('node:fs') ${emoji}`;",
|
|
},
|
|
{
|
|
name: "nested harmless template text",
|
|
code: "return `outer ${`require('node:fs')`}`;",
|
|
},
|
|
{
|
|
name: "object braces inside a template expression",
|
|
code: "return `outer ${{ value: `import('node:fs')` }.value}`;",
|
|
},
|
|
{
|
|
name: "quoted module text inside a template expression",
|
|
code: "return `outer ${\"require('node:fs')\"}`;",
|
|
},
|
|
{
|
|
name: "line-commented module access",
|
|
code: "// require('node:fs')\nreturn 7;",
|
|
},
|
|
{
|
|
name: "block-commented module access",
|
|
code: "/* import('node:fs') */ return 7;",
|
|
},
|
|
{
|
|
name: "quoted import.meta text",
|
|
code: 'return "import.meta.url";',
|
|
},
|
|
{
|
|
name: "module-shaped regular expression",
|
|
code: 'return /import.meta/.test("import.meta");',
|
|
},
|
|
{
|
|
name: "module-shaped regular expression after an assignment",
|
|
code: 'const pattern = /import.meta/; return pattern.test("import.meta");',
|
|
},
|
|
{
|
|
name: "module-shaped regular expression in a template expression",
|
|
code: 'return `${/import.meta/.test("import.meta")}`;',
|
|
},
|
|
{
|
|
name: "module-shaped regular expression after division",
|
|
code: "return 10 / /import.meta/.source.length;",
|
|
},
|
|
{
|
|
name: "module-shaped regular expression after a control condition",
|
|
code: 'if (true) /import.meta/.test("import.meta"); return 7;',
|
|
},
|
|
{
|
|
name: "module-shaped regular expression after nested control parentheses",
|
|
code: 'if ((true)) /import.meta/.test("import.meta"); return 7;',
|
|
},
|
|
{
|
|
name: "regular-expression character class with a slash",
|
|
code: 'return /[a/]import.meta/.test("aimport.meta");',
|
|
},
|
|
{
|
|
name: "regular expression after postfix-increment division",
|
|
code: "let value = 10; return value++ / /import.meta/.source.length;",
|
|
},
|
|
{
|
|
name: "regular expression after postfix-decrement division",
|
|
code: "let value = 10; return value-- / /import.meta/.source.length;",
|
|
},
|
|
{
|
|
name: "regular expression after contextual member division",
|
|
code: "const value = { of: 10 }; return value.of / /import.meta/.source.length;",
|
|
},
|
|
{
|
|
name: "regular expression after a keyword-shaped control method",
|
|
code: "const value = { if() { return 10; } }; return value.if() / /import.meta/.source.length;",
|
|
},
|
|
{
|
|
name: "regular expression after an optional keyword-shaped control method",
|
|
code: "const value = { if() { return 10; } }; return value?.if() / /import.meta/.source.length;",
|
|
},
|
|
{
|
|
name: "regular expression after a nested contextual await identifier",
|
|
code: "function run() { const await = 10; return await / /import.meta/.source.length; } return run();",
|
|
},
|
|
{
|
|
name: "regular expression after a keyword-shaped private member",
|
|
code: "class Guest { #return = 10; run() { return this.#return / /import.meta/.source.length; } } return new Guest().run();",
|
|
},
|
|
{
|
|
name: "ordinary import method",
|
|
code: "const api = { import(value) { return value; } }; return api.import(42);",
|
|
},
|
|
{
|
|
name: "ordinary require method",
|
|
code: "const api = { require(value) { return value; } }; return api.require(42);",
|
|
},
|
|
{
|
|
name: "optional ordinary import method",
|
|
code: "const api = { import(value) { return value; } }; return api?.import?.(42);",
|
|
},
|
|
{
|
|
name: "computed ordinary require method",
|
|
code: 'const api = { require(value) { return value; } }; return api["require"](42);',
|
|
},
|
|
{
|
|
name: "ordinary import metadata property",
|
|
code: "const api = { import: { meta: 42 } }; return api.import.meta;",
|
|
},
|
|
{
|
|
name: "ordinary malformed JavaScript for guest syntax diagnostics",
|
|
code: "const answer = ;",
|
|
},
|
|
])("preserves $name", async ({ code }) => {
|
|
await expect(prepareSource({ code, config })).resolves.toBe(code);
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: "direct require",
|
|
code: "return require('node:fs');",
|
|
},
|
|
{
|
|
name: "direct dynamic import",
|
|
code: "return import('node:fs');",
|
|
},
|
|
{
|
|
name: "direct import.meta",
|
|
code: "return import.meta.url;",
|
|
},
|
|
{
|
|
name: "comment-separated require",
|
|
code: "return require /* hidden */ ('node:fs');",
|
|
},
|
|
{
|
|
name: "Unicode-escaped direct require",
|
|
code: String.raw`return r\u0065quire('node:fs');`,
|
|
},
|
|
{
|
|
name: "optional direct require",
|
|
code: "return require?.('node:fs');",
|
|
},
|
|
{
|
|
name: "parenthesized direct require",
|
|
code: "return (require)('node:fs');",
|
|
},
|
|
{
|
|
name: "sequence-wrapped direct require",
|
|
code: "return (0, require)('node:fs');",
|
|
},
|
|
{
|
|
name: "comment-separated dynamic import",
|
|
code: "return import /* hidden */ ('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import in template interpolation",
|
|
code: "return `${import('node:fs')}`;",
|
|
},
|
|
{
|
|
name: "require in template interpolation",
|
|
code: "return `${require('node:fs')}`;",
|
|
},
|
|
{
|
|
name: "dynamic import in nested template interpolation",
|
|
code: "return `${`nested ${import('node:fs')}`}`;",
|
|
},
|
|
{
|
|
name: "require in nested template interpolation",
|
|
code: "return `${`nested ${require('node:fs')}`}`;",
|
|
},
|
|
{
|
|
name: "dynamic import inside template-expression object braces",
|
|
code: "return `${({ value: import('node:fs') }).value}`;",
|
|
},
|
|
{
|
|
name: "require after a harmless template",
|
|
code: "const message = `import('node:fs')`; return require('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a harmless regular expression",
|
|
code: "const pattern = /import.meta/; return import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after division",
|
|
code: "return 10 / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a regex and control condition",
|
|
code: "if (true) /import.meta/.test('x'); return import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after postfix-increment division",
|
|
code: "let value = 1; return value++ / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after postfix-decrement division",
|
|
code: "let value = 1; return value-- / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a contextual of property",
|
|
code: "const value = { of: 1 }; return value.of / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a keyword-shaped return property",
|
|
code: "const value = { return: 1 }; return value.return / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a keyword-shaped control method",
|
|
code: "const value = { if() { return 1; } }; return value.if() / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after an optional keyword-shaped return property",
|
|
code: "const value = { return: 1 }; return value?.return / import('node:fs') / 1;",
|
|
},
|
|
{
|
|
name: "require after an optional keyword-shaped return property",
|
|
code: "const value = { return: 1 }; return value?.return / require('node:fs') / 1;",
|
|
},
|
|
{
|
|
name: "dynamic import after an optional keyword-shaped control method",
|
|
code: "const value = { if() { return 1; } }; return value?.if() / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a contextual of identifier",
|
|
code: "const of = 1; return of / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a contextual yield identifier",
|
|
code: "const yield = 1; return yield / import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after a nested contextual await identifier",
|
|
code: "function run() { const await = 1; return await / (globalThis.pending = import('node:fs')); } run(); return globalThis.pending;",
|
|
},
|
|
{
|
|
name: "dynamic import after a keyword-shaped private member",
|
|
code: "class Guest { #return = 1; run() { return this.#return / (globalThis.pending = import('node:fs')); } } new Guest().run(); return globalThis.pending;",
|
|
},
|
|
{
|
|
name: "require after a nested contextual await identifier",
|
|
code: "function run() { const await = 1; return await / require('node:fs'); } return run();",
|
|
},
|
|
{
|
|
name: "malformed input containing an executable module loader",
|
|
code: "const answer = ; return import('node:fs');",
|
|
},
|
|
{
|
|
name: "dynamic import after an astral-filled TypeScript string",
|
|
code: `const label: string = "${"😀".repeat(96)}"; return import('node:fs');`,
|
|
},
|
|
{
|
|
name: "require after an astral-filled TypeScript string",
|
|
code: `const label: string = "${"😀".repeat(96)}"; return require('node:fs');`,
|
|
},
|
|
])("rejects $name", async ({ code }) => {
|
|
await expect(prepareSource({ code, config })).rejects.toThrow(
|
|
"code mode module access is disabled",
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: "module-shaped regular expression after a type annotation",
|
|
code: 'const value: number = 1; return /import.meta/.test("import.meta");',
|
|
},
|
|
{
|
|
name: "module-shaped regular expression after astral Unicode",
|
|
code: `const value: number = 1; const padding = "${"😀".repeat(12)}"; return /import.meta/.test("import.meta");`,
|
|
},
|
|
{
|
|
name: "regular expression after an optional keyword-shaped property",
|
|
code: "const value: { return: number } = { return: 10 }; return value?.return / /import.meta/.source.length;",
|
|
},
|
|
{
|
|
name: "module-shaped nested template text",
|
|
code: "const value: number = 1; return `outer ${`import('node:fs')`}`;",
|
|
},
|
|
{
|
|
name: "module-shaped comment",
|
|
code: "const value: number = 1; /* import('node:fs') */ return value;",
|
|
},
|
|
{
|
|
name: "ordinary typed import method",
|
|
code: "const api: { import(value: number): number } = { import(value) { return value; } }; return api.import(42);",
|
|
},
|
|
{
|
|
name: "ordinary typed require method",
|
|
code: "const api: { require(value: number): number } = { require(value) { return value; } }; return api.require(42);",
|
|
},
|
|
])("preserves TypeScript $name", async ({ code }) => {
|
|
await expect(prepareSource({ code, language: "typescript", config })).resolves.toEqual(
|
|
expect.any(String),
|
|
);
|
|
});
|
|
|
|
it("separates every deterministic literal and executable module-shaped input", async () => {
|
|
const moduleExpressions = [
|
|
"require('node:fs')",
|
|
"import('node:fs')",
|
|
"import.meta.url",
|
|
'require /* comment */ ("node:fs")',
|
|
'import /* comment */ ("node:fs")',
|
|
];
|
|
|
|
for (const expression of moduleExpressions) {
|
|
for (const harmless of [
|
|
`return ${JSON.stringify(expression)};`,
|
|
`return \`literal ${expression}\`;`,
|
|
]) {
|
|
await expect(prepareSource({ code: harmless, config })).resolves.toBe(harmless);
|
|
}
|
|
for (const executable of [`return ${expression};`, `return \`value \${${expression}}\`;`]) {
|
|
await expect(prepareSource({ code: executable, config })).rejects.toThrow(
|
|
"code mode module access is disabled",
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("distinguishes every adversarial division and regular-expression context", async () => {
|
|
const divisionContexts = [
|
|
{ prefix: "let value = 10; return value++", suffix: "" },
|
|
{ prefix: "let value = 10; return value--", suffix: "" },
|
|
{ prefix: "const value = { of: 10 }; return value.of", suffix: "" },
|
|
{ prefix: "const value = { return: 10 }; return value.return", suffix: "" },
|
|
{ prefix: "const value = { if() { return 10; } }; return value.if()", suffix: "" },
|
|
{ prefix: "const value = { if() { return 10; } }; return value?.if()", suffix: "" },
|
|
{ prefix: "const of = 10; return of", suffix: "" },
|
|
{ prefix: "const yield = 10; return yield", suffix: "" },
|
|
{
|
|
prefix: "function run() { const await = 10; return await",
|
|
suffix: " } return run();",
|
|
},
|
|
{
|
|
prefix: "class Guest { #return = 10; run() { return this.#return",
|
|
suffix: " } } return new Guest().run();",
|
|
},
|
|
];
|
|
|
|
for (const { prefix, suffix } of divisionContexts) {
|
|
const harmless = `${prefix} / /import.meta/.source.length;${suffix}`;
|
|
await expect(prepareSource({ code: harmless, config })).resolves.toBe(harmless);
|
|
|
|
const executable = `${prefix} / import('node:fs');${suffix}`;
|
|
await expect(prepareSource({ code: executable, config })).rejects.toThrow(
|
|
"code mode module access is disabled",
|
|
);
|
|
}
|
|
});
|
|
|
|
it("separates ordinary methods from every disguised module loader", async () => {
|
|
const harmlessMethods = [
|
|
"api.import(value)",
|
|
"api.require(value)",
|
|
"api?.import?.(value)",
|
|
'api["require"](value)',
|
|
];
|
|
const moduleExpressions = [
|
|
String.raw`r\u0065quire('node:fs')`,
|
|
"require?.('node:fs')",
|
|
"(require)('node:fs')",
|
|
"(0, require)('node:fs')",
|
|
];
|
|
|
|
for (const index of [0, 1, 9_999]) {
|
|
for (const method of harmlessMethods) {
|
|
const harmless = `const value = ${index}; const api = { import(value) { return value; }, require(value) { return value; } }; return ${method};`;
|
|
await expect(prepareSource({ code: harmless, config })).resolves.toBe(harmless);
|
|
}
|
|
}
|
|
for (const expression of moduleExpressions) {
|
|
const executable = `return ${expression};`;
|
|
await expect(prepareSource({ code: executable, config })).rejects.toThrow(
|
|
"code mode module access is disabled",
|
|
);
|
|
}
|
|
});
|
|
|
|
it("rejects every Unicode-shifted TypeScript module-access offset", async () => {
|
|
for (let length = 1; length <= 96; length += 1) {
|
|
const padding = "😀".repeat(length);
|
|
for (const access of ["import('node:fs')", "require('node:fs')"]) {
|
|
await expect(
|
|
prepareSource({
|
|
code: `const label: string = "${padding}"; return ${access};`,
|
|
language: "typescript",
|
|
config,
|
|
}),
|
|
).rejects.toThrow("code mode module access is disabled");
|
|
}
|
|
}
|
|
}, 30_000);
|
|
});
|