Simplify method signature. (#15114)

This commit is contained in:
Tommaso Sciortino
2025-12-15 13:18:04 -08:00
committed by GitHub
parent d236df5b21
commit bb0c0d8ee3
17 changed files with 184 additions and 213 deletions

View File

@@ -82,7 +82,7 @@ describe('extension reloading', () => {
writeFileSync(testServerPath, safeJsonStringify(extension, 2));
// Start the CLI.
const run = await rig.runInteractive('--debug');
const run = await rig.runInteractive({ args: '--debug' });
await run.expectText('You have 1 extension with an update available');
// See the outdated extension
await run.sendText('/extensions list');

View File

@@ -24,9 +24,9 @@ describe('file-system', () => {
});
rig.createFile('test.txt', 'hello world');
const result = await rig.run(
`read the file test.txt and show me its contents`,
);
const result = await rig.run({
args: `read the file test.txt and show me its contents`,
});
const foundToolCall = await rig.waitForToolCall('read_file');
@@ -53,7 +53,9 @@ describe('file-system', () => {
});
rig.createFile('test.txt', '');
const result = await rig.run(`edit test.txt to have a hello world message`);
const result = await rig.run({
args: `edit test.txt to have a hello world message`,
});
// Accept multiple valid tools for editing files
const foundToolCall = await rig.waitForAnyToolCall([
@@ -109,9 +111,9 @@ describe('file-system', () => {
});
const fileName = 'my test file.txt';
const result = await rig.run(
`write "hello" to "${fileName}" and then stop. Do not perform any other actions.`,
);
const result = await rig.run({
args: `write "hello" to "${fileName}" and then stop. Do not perform any other actions.`,
});
const foundToolCall = await rig.waitForToolCall('write_file');
if (!foundToolCall) {
@@ -134,7 +136,7 @@ describe('file-system', () => {
rig.createFile(fileName, '1.0.0');
const prompt = `Read the version from ${fileName} and write the next version 1.0.1 back to the file.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
await rig.waitForTelemetryReady();
const toolLogs = rig.readToolLogs();
@@ -169,9 +171,9 @@ describe('file-system', () => {
const expectedContent = 'Hey there, \nnew line\nnew line';
rig.createFile(fileName, fileContent);
const result = await rig.run(
`rewrite the file ${fileName} to replace all instances of "test line" with "new line"`,
);
const result = await rig.run({
args: `rewrite the file ${fileName} to replace all instances of "test line" with "new line"`,
});
const validTools = ['write_file', 'edit'];
const foundToolCall = await rig.waitForAnyToolCall(validTools);
@@ -220,7 +222,9 @@ describe('file-system', () => {
);
const fileName = 'non_existent.txt';
const result = await rig.run(`In ${fileName}, replace "a" with "b"`);
const result = await rig.run({
args: `In ${fileName}, replace "a" with "b"`,
});
await rig.waitForTelemetryReady();
const toolLogs = rig.readToolLogs();

View File

@@ -24,7 +24,7 @@ describe('web search tool', () => {
let result;
try {
result = await rig.run(`what is the weather in London`);
result = await rig.run({ args: `what is the weather in London` });
} catch (error) {
// Network errors can occur in CI environments
if (

View File

@@ -55,8 +55,9 @@ describe('Hooks System Integration', () => {
},
);
const prompt = 'Create a file called test.txt with content "Hello World"';
const result = await rig.run(prompt);
const result = await rig.run({
args: 'Create a file called test.txt with content "Hello World"',
});
// The hook should block the write_file tool
const toolLogs = rig.readToolLogs();
@@ -107,9 +108,9 @@ describe('Hooks System Integration', () => {
},
);
const prompt =
'Create a file called approved.txt with content "Approved content"';
await rig.run(prompt);
await rig.run({
args: 'Create a file called approved.txt with content "Approved content"',
});
// The hook should allow the write_file tool
const foundWriteFile = await rig.waitForToolCall('write_file');
@@ -158,9 +159,9 @@ describe('Hooks System Integration', () => {
// Create a test file to read
rig.createFile('test-file.txt', 'This is test content');
const prompt =
'Read the contents of test-file.txt and tell me what it contains';
await rig.run(prompt);
await rig.run({
args: 'Read the contents of test-file.txt and tell me what it contains',
});
// Should find read_file tool call
const foundReadFile = await rig.waitForToolCall('read_file');
@@ -232,8 +233,7 @@ echo '{
},
});
const prompt = 'Tell me a story';
const result = await rig.run(prompt);
const result = await rig.run({ args: 'Tell me a story' });
// The hook should have replaced the request entirely
// Verify that the model responded to the modified request, not the original
@@ -316,8 +316,7 @@ echo '{
},
});
const prompt = 'What is 2 + 2?';
const result = await rig.run(prompt);
const result = await rig.run({ args: 'What is 2 + 2?' });
// The hook should have replaced the model response
expect(result).toContain(
@@ -374,9 +373,9 @@ echo '{
// Create a test file
rig.createFile('new_file_data.txt', 'test data');
const prompt =
'Check the content of new_file_data.txt, after that run echo command to see the content';
await rig.run(prompt);
await rig.run({
args: 'Check the content of new_file_data.txt, after that run echo command to see the content',
});
// Should use read_file (allowed) but not run_shell_command (not in allowed list)
const foundReadFile = await rig.waitForToolCall('read_file');
@@ -440,8 +439,7 @@ echo '{
},
});
const prompt = 'Hello, how are you?';
const result = await rig.run(prompt);
const result = await rig.run({ args: 'Hello, how are you?' });
// The hook should have added security context, which should influence the response
expect(result).toContain('security');
@@ -585,8 +583,7 @@ echo '{
},
});
const prompt = 'Hello, please help me with a task';
await rig.run(prompt);
await rig.run({ args: 'Hello, please help me with a task' });
// Should generate hook telemetry
const hookTelemetryFound = await rig.waitForTelemetryEvent('hook_call');
@@ -663,8 +660,9 @@ fi`;
},
});
const prompt = 'Create a file called input-test.txt with content "test"';
await rig.run(prompt);
await rig.run({
args: 'Create a file called input-test.txt with content "test"',
});
// Hook should validate input format successfully
const foundWriteFile = await rig.waitForToolCall('write_file');
@@ -739,11 +737,12 @@ fi`;
},
});
const prompt =
'Create a file called multi-event-test.txt with content ' +
'"testing multiple events", and then please reply with ' +
'everything I say just after this:"';
const result = await rig.run(prompt);
const result = await rig.run({
args:
'Create a file called multi-event-test.txt with content ' +
'"testing multiple events", and then please reply with ' +
'everything I say just after this:"',
});
// Should execute write_file tool
const foundWriteFile = await rig.waitForToolCall('write_file');
@@ -834,9 +833,9 @@ fi`;
},
});
const prompt =
'Create a file called error-test.txt with content "testing error handling"';
await rig.run(prompt);
await rig.run({
args: 'Create a file called error-test.txt with content "testing error handling"',
});
// Despite one hook failing, the working hook should still allow the operation
const foundWriteFile = await rig.waitForToolCall('write_file');
@@ -883,8 +882,7 @@ fi`;
},
});
const prompt = 'Create a file called telemetry-test.txt';
await rig.run(prompt);
await rig.run({ args: 'Create a file called telemetry-test.txt' });
// Should execute the tool
const foundWriteFile = await rig.waitForToolCall('write_file');
@@ -929,8 +927,7 @@ fi`;
});
// Run a simple query - the SessionStart hook will fire during app initialization
const prompt = 'Say hello';
await rig.run(prompt);
await rig.run({ args: 'Say hello' });
// Verify hook executed with correct parameters
const hookLogs = rig.readHookLogs();
@@ -1174,8 +1171,7 @@ fi`;
});
// Run a simple query that will trigger automatic compression
const prompt = 'Say hello in exactly 5 words';
await rig.run(prompt);
await rig.run({ args: 'Say hello in exactly 5 words' });
// Verify hook executed with correct parameters
const hookLogs = rig.readHookLogs();
@@ -1236,8 +1232,7 @@ fi`;
});
// Run in non-interactive mode with a simple prompt
const prompt = 'Hello';
await rig.run(prompt);
await rig.run({ args: 'Hello' });
// The process should exit gracefully, firing the SessionEnd hook
// Wait for telemetry to be written to disk
@@ -1344,9 +1339,9 @@ echo '{"decision": "block", "systemMessage": "Disabled hook should not execute",
},
});
const prompt =
'Create a file called disabled-test.txt with content "test"';
const result = await rig.run(prompt);
const result = await rig.run({
args: 'Create a file called disabled-test.txt with content "test"',
});
// Tool should execute (enabled hook allows it)
const foundWriteFile = await rig.waitForToolCall('write_file');
@@ -1431,8 +1426,9 @@ echo '{"decision": "block", "systemMessage": "Disabled hook should not execute",
);
// First run - only active hook should execute
const prompt1 = 'Create a file called first-run.txt with "test1"';
const result1 = await rig.run(prompt1);
const result1 = await rig.run({
args: 'Create a file called first-run.txt with "test1"',
});
// Tool should execute (active hook allows it)
const foundWriteFile1 = await rig.waitForToolCall('write_file');
@@ -1455,8 +1451,9 @@ echo '{"decision": "block", "systemMessage": "Disabled hook should not execute",
expect(disabledHookLog1).toBeUndefined();
// Second run - verify disabled hook stays disabled
const prompt2 = 'Create a file called second-run.txt with "test2"';
const result2 = await rig.run(prompt2);
const result2 = await rig.run({
args: 'Create a file called second-run.txt with "test2"',
});
const foundWriteFile2 = await rig.waitForToolCall('write_file');
expect(foundWriteFile2).toBeTruthy();

View File

@@ -22,11 +22,9 @@ describe('JSON output', () => {
});
it('should return a valid JSON with response and stats', async () => {
const result = await rig.run(
'What is the capital of France?',
'--output-format',
'json',
);
const result = await rig.run({
args: ['What is the capital of France?', '--output-format', 'json'],
});
const parsed = JSON.parse(result);
expect(parsed).toHaveProperty('response');
@@ -38,7 +36,9 @@ describe('JSON output', () => {
});
it('should return a valid JSON with a session ID', async () => {
const result = await rig.run('Hello', '--output-format', 'json');
const result = await rig.run({
args: ['Hello', '--output-format', 'json'],
});
const parsed = JSON.parse(result);
expect(parsed).toHaveProperty('session_id');
@@ -58,7 +58,7 @@ describe('JSON output', () => {
let thrown: Error | undefined;
try {
await rig.run('Hello', '--output-format', 'json');
await rig.run({ args: ['Hello', '--output-format', 'json'] });
expect.fail('Expected process to exit with error');
} catch (e) {
thrown = e as Error;
@@ -108,12 +108,14 @@ describe('JSON output', () => {
'json-output.error.responses',
),
});
const result = await rig.run(
`Read the contents of ${rig.testDir}/path/to/nonexistent/file.txt and tell me what it says. ` +
'On error, respond to the user with exactly the text "File not found".',
'--output-format',
'json',
);
const result = await rig.run({
args: [
`Read the contents of ${rig.testDir}/path/to/nonexistent/file.txt and tell me what it says. ` +
'On error, respond to the user with exactly the text "File not found".',
'--output-format',
'json',
],
});
const parsed = JSON.parse(result);

View File

@@ -45,7 +45,7 @@ describe('list_directory', () => {
const prompt = `Can you list the files in the current directory.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
try {
await rig.expectToolCallSuccess(['list_directory']);

View File

@@ -23,12 +23,10 @@ describe('mixed input crash prevention', () => {
const stdinContent = "say '1'.";
try {
await rig.run(
{ stdin: stdinContent },
'--prompt-interactive',
"say '2'.",
"say '3'.",
);
await rig.run({
args: ['--prompt-interactive', "say '2'.", "say '3'."],
stdin: stdinContent,
});
throw new Error('Expected the command to fail, but it succeeded');
} catch (error: unknown) {
expect(error).toBeInstanceOf(Error);
@@ -50,11 +48,10 @@ describe('mixed input crash prevention', () => {
rig.setup('should provide clear error message for mixed input');
try {
await rig.run(
{ stdin: 'test input' },
'--prompt-interactive',
'test prompt',
);
await rig.run({
args: ['--prompt-interactive', 'test prompt'],
stdin: 'test input',
});
throw new Error('Expected the command to fail, but it succeeded');
} catch (error: unknown) {
expect(error).toBeInstanceOf(Error);

View File

@@ -25,7 +25,7 @@ describe('read_many_files', () => {
const prompt = `Use the read_many_files tool to read the contents of file1.txt and file2.txt and then print the contents of each file.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
// Check for either read_many_files or multiple read_file calls
const allTools = rig.readToolLogs();

View File

@@ -26,7 +26,9 @@ describe('replace', () => {
rig.createFile(fileName, originalContent);
await rig.run(`Replace 'foo' with 'bar' in the file 'file_to_replace.txt'`);
await rig.run({
args: `Replace 'foo' with 'bar' in the file 'file_to_replace.txt'`,
});
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
@@ -46,9 +48,9 @@ describe('replace', () => {
rig.createFile(fileName, originalContent);
await rig.run(
"Open regex.yml and append ' # updated' after the line containing ^[sv]d[a-z]$ without breaking the $ character.",
);
await rig.run({
args: "Open regex.yml and append ' # updated' after the line containing ^[sv]d[a-z]$ without breaking the $ character.",
});
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
@@ -68,7 +70,7 @@ describe('replace', () => {
rig.createFile(fileName, originalContent);
const prompt = `In ${fileName}, replace "<INSERT_TEXT_HERE>" with:\n${newBlock}. Use unix style line endings.`;
await rig.run(prompt);
await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();
@@ -87,9 +89,9 @@ describe('replace', () => {
const expectedContent = 'Hello\nWorld';
rig.createFile(fileName, originalContent);
await rig.run(
`In ${fileName}, delete the entire block from "## DELETE THIS ##" to "## END DELETE ##" including the markers and the newline that follows it.`,
);
await rig.run({
args: `In ${fileName}, delete the entire block from "## DELETE THIS ##" to "## END DELETE ##" including the markers and the newline that follows it.`,
});
const foundToolCall = await rig.waitForToolCall('replace');
expect(foundToolCall, 'Expected to find a replace tool call').toBeTruthy();

View File

@@ -98,7 +98,7 @@ describe('run_shell_command', () => {
const prompt = `Please run the command "echo hello-world" and show me the output`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('run_shell_command');
@@ -161,13 +161,11 @@ describe('run_shell_command', () => {
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
// Provide the prompt via stdin to simulate non-interactive mode
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${tool})`,
);
const result = await rig.run({
args: [`--allowed-tools=run_shell_command(${tool})`],
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -206,13 +204,11 @@ describe('run_shell_command', () => {
const { command } = getLineCountCommand();
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
'--allowed-tools=run_shell_command',
);
const result = await rig.run({
args: '--allowed-tools=run_shell_command',
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -245,7 +241,7 @@ describe('run_shell_command', () => {
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
const result = await rig.run({
prompt: prompt,
args: prompt,
yolo: true,
});
@@ -277,13 +273,11 @@ describe('run_shell_command', () => {
const { tool, command } = getLineCountCommand();
const prompt = `use ${command} to tell me how many lines there are in ${testFile}`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=ShellTool(${tool})`,
);
const result = await rig.run({
args: `--allowed-tools=ShellTool(${tool})`,
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -325,14 +319,14 @@ describe('run_shell_command', () => {
`use both ${command} and ls to count the number of lines in files in this ` +
`directory. Do not pipe these commands into each other, run them separately.`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command(ls)',
);
const result = await rig.run({
args: [
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command(ls)',
],
stdin: prompt,
yolo: false,
});
for (const expected in ['ls', tool]) {
const foundToolCall = await rig.waitForToolCall(
@@ -380,13 +374,11 @@ describe('run_shell_command', () => {
`If the command fails because it is not permitted, respond with the single word FAIL. ` +
`If it succeeds, respond with SUCCESS.`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${allowedCommand})`,
);
const result = await rig.run({
args: `--allowed-tools=run_shell_command(${allowedCommand})`,
stdin: prompt,
yolo: false,
});
if (!result.toLowerCase().includes('fail')) {
printDebugInfo(rig, result, {
@@ -443,13 +435,11 @@ describe('run_shell_command', () => {
const chained = getChainedEchoCommand();
const shellInjection = `!{${chained.command}}`;
await rig.run(
{
stdin: `${shellInjection}\n`,
yolo: false,
},
`--allowed-tools=ShellTool(${chained.allowPattern})`,
);
await rig.run({
args: `--allowed-tools=ShellTool(${chained.allowPattern})`,
stdin: `${shellInjection}\n`,
yolo: false,
});
// CLI should refuse to execute the chained command without scheduling run_shell_command.
const toolLogs = rig
@@ -474,14 +464,14 @@ describe('run_shell_command', () => {
const { tool } = getLineCountCommand();
const prompt = `Please run the command "echo test-allow-all" and show me the output`;
const result = await rig.run(
{
stdin: prompt,
yolo: false,
},
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command',
);
const result = await rig.run({
args: [
`--allowed-tools=run_shell_command(${tool})`,
'--allowed-tools=run_shell_command',
],
stdin: prompt,
yolo: false,
});
const foundToolCall = await rig.waitForToolCall('run_shell_command', 15000);
@@ -523,7 +513,7 @@ describe('run_shell_command', () => {
try {
const prompt = `Use echo to learn the value of the environment variable named ${varName} and tell me what it is.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('run_shell_command');
@@ -551,7 +541,7 @@ describe('run_shell_command', () => {
rig.createFile(fileName, 'test content');
const prompt = `Run a shell command to list the files in the current directory and tell me what they are.`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('run_shell_command');
@@ -577,9 +567,9 @@ describe('run_shell_command', () => {
settings: { tools: { core: ['run_shell_command'] } },
});
const invalidCommand = getInvalidCommand();
const result = await rig.run(
`I am testing the error handling of the run_shell_command tool. Please attempt to run the following command, which I know has invalid syntax: \`${invalidCommand}\`. If the command fails as expected, please return the word FAIL, otherwise return the word SUCCESS.`,
);
const result = await rig.run({
args: `I am testing the error handling of the run_shell_command tool. Please attempt to run the following command, which I know has invalid syntax: \`${invalidCommand}\`. If the command fails as expected, please return the word FAIL, otherwise return the word SUCCESS.`,
});
expect(result).toContain('FAIL');
const escapedInvalidCommand = JSON.stringify(invalidCommand).slice(1, -1);

View File

@@ -24,7 +24,7 @@ describe('save_memory', () => {
const prompt = `remember that my favorite color is blue.
what is my favorite color? tell me that and surround it with $ symbol`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('save_memory');

View File

@@ -218,9 +218,9 @@ describe('simple-mcp-server', () => {
// Test directory is already set up in before hook
// Just run the command - MCP server config is in settings.json
const output = await rig.run(
'Use the `add` tool to calculate 5+10 and output only the resulting number.',
);
const output = await rig.run({
args: 'Use the `add` tool to calculate 5+10 and output only the resulting number.',
});
const foundToolCall = await rig.waitForToolCall('add');

View File

@@ -23,7 +23,7 @@ describe.skip('stdin context', () => {
const stdinContent = `When I ask you for a token respond with ${randomString}`;
const prompt = 'Can I please have a token?';
const result = await rig.run({ prompt, stdin: stdinContent });
const result = await rig.run({ args: prompt, stdin: stdinContent });
await rig.waitForTelemetryEvent('api_request');
const lastRequest = rig.readLastApiRequest();

View File

@@ -20,7 +20,7 @@ describe('telemetry', () => {
rig.setup('should emit a metric and a log event');
// Run a simple command that should trigger telemetry
await rig.run('just saying hi');
await rig.run({ args: 'just saying hi' });
// Verify that a user_prompt event was logged
const hasUserPromptEvent = await rig.waitForTelemetryEvent('user_prompt');

View File

@@ -18,6 +18,7 @@ import * as os from 'node:os';
import { GEMINI_DIR } from '../packages/core/src/utils/paths.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const BUNDLE_PATH = join(__dirname, '..', 'bundle/gemini.js');
// Get timeout based on environment
function getDefaultTimeout() {
@@ -266,8 +267,7 @@ export class InteractiveRun {
}
export class TestRig {
bundlePath: string;
testDir: string | null;
testDir: string | null = null;
testName?: string;
_lastRunStdout?: string;
// Path to the copied fake responses file for this test.
@@ -276,11 +276,6 @@ export class TestRig {
originalFakeResponsesPath?: string;
private _interactiveRuns: InteractiveRun[] = [];
constructor() {
this.bundlePath = join(__dirname, '..', 'bundle/gemini.js');
this.testDir = null;
}
setup(
testName: string,
options: {
@@ -370,7 +365,7 @@ export class TestRig {
const command = isNpmReleaseTest ? 'gemini' : 'node';
const initialArgs = isNpmReleaseTest
? extraInitialArgs
: [this.bundlePath, ...extraInitialArgs];
: [BUNDLE_PATH, ...extraInitialArgs];
if (this.fakeResponsesPath) {
if (process.env['REGENERATE_MODEL_GOLDENS'] === 'true') {
initialArgs.push('--record-responses', this.fakeResponsesPath);
@@ -381,19 +376,13 @@ export class TestRig {
return { command, initialArgs };
}
run(
promptOrOptions:
| string
| {
prompt?: string;
stdin?: string;
stdinDoesNotEnd?: boolean;
yolo?: boolean;
},
...args: string[]
): Promise<string> {
const yolo =
typeof promptOrOptions === 'string' || promptOrOptions.yolo !== false;
run(options: {
args?: string | string[];
stdin?: string;
stdinDoesNotEnd?: boolean;
yolo?: boolean;
}): Promise<string> {
const yolo = options.yolo !== false;
const { command, initialArgs } = this._getCommandAndArgs(
yolo ? ['--yolo'] : [],
);
@@ -407,21 +396,17 @@ export class TestRig {
encoding: 'utf-8',
};
if (typeof promptOrOptions === 'string') {
commandArgs.push(promptOrOptions);
} else if (
typeof promptOrOptions === 'object' &&
promptOrOptions !== null
) {
if (promptOrOptions.prompt) {
commandArgs.push(promptOrOptions.prompt);
}
if (promptOrOptions.stdin) {
execOptions.input = promptOrOptions.stdin;
if (options.args) {
if (Array.isArray(options.args)) {
commandArgs.push(...options.args);
} else {
commandArgs.push(options.args);
}
}
commandArgs.push(...args);
if (options.stdin) {
execOptions.input = options.stdin;
}
const child = spawn(command, commandArgs, {
cwd: this.testDir!,
@@ -437,10 +422,7 @@ export class TestRig {
child.stdin!.write(execOptions.input);
}
if (
typeof promptOrOptions === 'object' &&
!promptOrOptions.stdinDoesNotEnd
) {
if (!options.stdinDoesNotEnd) {
child.stdin!.end();
}
@@ -1032,26 +1014,23 @@ export class TestRig {
return null;
}
async runInteractive(
options?: { yolo?: boolean } | string,
...args: string[]
): Promise<InteractiveRun> {
// Handle backward compatibility: if first param is a string, treat as arg
let yolo = true; // Default to YOLO mode
let additionalArgs: string[] = args;
if (typeof options === 'string') {
// Old-style call: runInteractive('--debug')
additionalArgs = [options, ...args];
} else if (typeof options === 'object' && options !== null) {
// New-style call: runInteractive({ yolo: false })
yolo = options.yolo !== false;
}
async runInteractive(options?: {
args?: string | string[];
yolo?: boolean;
}): Promise<InteractiveRun> {
const yolo = options?.yolo !== false;
const { command, initialArgs } = this._getCommandAndArgs(
yolo ? ['--yolo'] : [],
);
const commandArgs = [...initialArgs, ...additionalArgs];
const commandArgs = [...initialArgs];
if (options?.args) {
if (Array.isArray(options.args)) {
commandArgs.push(...options.args);
} else {
commandArgs.push(options.args);
}
}
const ptyOptions: pty.IPtyForkOptions = {
name: 'xterm-color',

View File

@@ -66,7 +66,7 @@ describe('BOM end-to-end integraion', () => {
) {
writeFileSync(join(rig.testDir!, filename), content);
const prompt = `read the file ${filename} and output its exact contents`;
const output = await rig.run(prompt);
const output = await rig.run({ args: prompt });
await rig.waitForToolCall('read_file');
const lower = output.toLowerCase();
if (expectedText === null) {
@@ -126,7 +126,7 @@ describe('BOM end-to-end integraion', () => {
const filename = 'gemini-screenshot.png';
writeFileSync(join(rig.testDir!, filename), imageContent);
const prompt = `What is shown in the image ${filename}?`;
const output = await rig.run(prompt);
const output = await rig.run({ args: prompt });
await rig.waitForToolCall('read_file');
const lower = output.toLowerCase();
// The response is non-deterministic, so we just check for some

View File

@@ -27,7 +27,7 @@ describe('write_file', () => {
});
const prompt = `show me an example of using the write tool. put a dad joke in dad.txt`;
const result = await rig.run(prompt);
const result = await rig.run({ args: prompt });
const foundToolCall = await rig.waitForToolCall('write_file');