Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/actions/publish-release/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ runs:
shell: 'bash'
run: |
npm publish \
--ignore-scripts \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_CORE_PACKAGE_NAME}" \
--tag staging-tmp
Expand Down Expand Up @@ -215,6 +216,7 @@ runs:
shell: 'bash'
run: |
npm publish \
--ignore-scripts \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
--tag staging-tmp
Expand Down Expand Up @@ -242,6 +244,7 @@ runs:
# Tag staging for initial release
run: |
npm publish \
--ignore-scripts \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
--tag staging-tmp
Expand Down
2 changes: 2 additions & 0 deletions integration-tests/parallel-tools.responses
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
{"method":"generateContent","response":{"candidates":[{"content":{"parts":[{"text":"{\n \"reasoning\": \"Simple task.\",\n \"model_choice\": \"flash\"\n}"}]},"finishReason":"STOP","index":0}]}}
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"read_file","args":{"file_path":"file1.txt"}}},{"functionCall":{"name":"read_file","args":{"file_path":"file2.txt"}}},{"functionCall":{"name":"write_file","args":{"file_path":"output.txt","content":"wave2"}}},{"functionCall":{"name":"read_file","args":{"file_path":"file3.txt"}}},{"functionCall":{"name":"read_file","args":{"file_path":"file4.txt"}}}, {"text":"All waves completed successfully."}]},"finishReason":"STOP","index":0}]}]}
{"method":"generateContent","response":{"candidates":[{"content":{"parts":[{"text":"All waves completed successfully."}]},"finishReason":"STOP","index":0}]}}
28 changes: 17 additions & 11 deletions integration-tests/parallel-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('Parallel Tool Execution Integration', () => {
it('should execute [read, read, write, read, read] in correct waves with user approval', async () => {
rig.setup('parallel-wave-execution', {
fakeResponsesPath: join(import.meta.dirname, 'parallel-tools.responses'),
fakeResponsesNonStrict: true,
settings: {
tools: {
core: ['read_file', 'write_file'],
Expand All @@ -40,19 +41,24 @@ describe('Parallel Tool Execution Integration', () => {

const run = await rig.runInteractive({ approvalMode: 'default' });

// 1. Trigger the wave
await run.type('ok');
await run.type('\r');
try {
// 1. Trigger the wave
await run.type('ok');
await run.type('\r');

// 3. Wait for the write_file prompt.
await run.expectText('Allow', 5000);
// 3. Wait for the write_file prompt.
await run.expectText('Allow', 10000);

// 4. Press Enter to approve the write_file.
await run.type('y');
await run.type('\r');
// 4. Press Enter to approve the write_file.
await run.type('y');
await run.type('\r');

// 5. Wait for the final model response
await run.expectText('All waves completed successfully.', 5000);
// 5. Wait for the final model response
await run.expectText('All waves completed successfully.', 10000);
} catch (err) {
fs.writeFileSync('pty_output_failure.txt', run.output);
throw err;
}

// Verify all tool calls were made and succeeded in the logs
await rig.expectToolCallSuccess(['write_file']);
Expand All @@ -73,5 +79,5 @@ describe('Parallel Tool Execution Integration', () => {
expect(fs.readFileSync(join(rig.testDir!, 'output.txt'), 'utf8')).toBe(
'wave2',
);
});
}, 30000);
});
2 changes: 1 addition & 1 deletion packages/core/src/scheduler/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ export class Scheduler {

if (isWaitingForExternal && this.state.isActive) {
// Yield to the event loop to allow external events (tool completion, user input) to progress.
await new Promise((resolve) => queueMicrotask(() => resolve(true)));
await new Promise((resolve) => setTimeout(resolve, 10));
return true;
}

Expand Down
6 changes: 6 additions & 0 deletions packages/test-utils/src/test-rig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ export class TestRig {
_lastRunStderr?: string;
// Path to the copied fake responses file for this test.
fakeResponsesPath?: string;
// Whether to run fake responses in non-strict mode.
fakeResponsesNonStrict?: boolean;
// Original fake responses file path for rewriting goldens in record mode.
originalFakeResponsesPath?: string;
private _interactiveRuns: InteractiveRun[] = [];
Expand All @@ -377,6 +379,7 @@ export class TestRig {
settings?: Record<string, unknown>;
state?: Record<string, unknown>;
fakeResponsesPath?: string;
fakeResponsesNonStrict?: boolean;
} = {},
) {
this.testName = testName;
Expand All @@ -398,6 +401,7 @@ export class TestRig {
if (options.fakeResponsesPath) {
this.fakeResponsesPath = join(this.testDir, 'fake-responses.json');
this.originalFakeResponsesPath = options.fakeResponsesPath;
this.fakeResponsesNonStrict = options.fakeResponsesNonStrict;
if (process.env['REGENERATE_MODEL_GOLDENS'] !== 'true') {
fs.copyFileSync(options.fakeResponsesPath, this.fakeResponsesPath);
}
Expand Down Expand Up @@ -558,6 +562,8 @@ export class TestRig {
if (this.fakeResponsesPath) {
if (process.env['REGENERATE_MODEL_GOLDENS'] === 'true') {
initialArgs.push('--record-responses', this.fakeResponsesPath);
} else if (this.fakeResponsesNonStrict) {
initialArgs.push('--fake-responses-non-strict', this.fakeResponsesPath);
} else {
initialArgs.push('--fake-responses', this.fakeResponsesPath);
}
Expand Down
Loading