Skip to content

Commit 030e2ff

Browse files
committed
add sleep tool.
1 parent b656900 commit 030e2ff

File tree

9 files changed

+95
-32
lines changed

9 files changed

+95
-32
lines changed

src/tools/getTools.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { subAgentTool } from '../tools/interaction/subAgent.js';
2-
import { readFileTool } from '../tools/io/readFile.js';
3-
import { userPromptTool } from '../tools/interaction/userPrompt.js';
4-
import { sequenceCompleteTool } from '../tools/system/sequenceComplete.js';
5-
import { fetchTool } from '../tools/io/fetch.js';
1+
import { subAgentTool } from './interaction/subAgent.js';
2+
import { readFileTool } from './io/readFile.js';
3+
import { userPromptTool } from './interaction/userPrompt.js';
4+
import { sequenceCompleteTool } from './system/sequenceComplete.js';
5+
import { fetchTool } from './io/fetch.js';
66
import { Tool } from '../core/types.js';
77
import { updateFileTool } from './io/updateFile.js';
88
import { shellStartTool } from './system/shellStart.js';
99
import { shellMessageTool } from './system/shellMessage.js';
1010
import { browseMessageTool } from './browser/browseMessage.js';
1111
import { browseStartTool } from './browser/browseStart.js';
1212
import { respawnTool } from './system/respawn.js';
13+
import { sleepTool } from './system/sleep.js';
1314

1415
export function getTools(): Tool[] {
1516
return [
@@ -24,5 +25,6 @@ export function getTools(): Tool[] {
2425
browseStartTool,
2526
browseMessageTool,
2627
respawnTool,
28+
sleepTool,
2729
] as Tool[];
2830
}

src/tools/index.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/tools/system/respawn.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ export const respawnTool: Tool = {
2323
type: 'string',
2424
description: 'A message indicating that the respawn has been initiated',
2525
},
26-
execute: async (
27-
params: Record<string, any>,
28-
context: ToolContext,
26+
execute: (
27+
_params: Record<string, any>,
28+
_context: ToolContext,
2929
): Promise<string> => {
3030
// This is a special case tool - the actual respawn logic is handled in toolAgent
31-
return 'Respawn initiated';
31+
return Promise.resolve('Respawn initiated');
3232
},
3333
};

src/tools/system/shellMessage.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
22
import { processStates, shellStartTool } from './shellStart.js';
33
import { MockLogger } from '../../utils/mockLogger.js';
44
import { shellMessageTool, NodeSignals } from './shellMessage.js';
5+
import { sleep } from '../../utils/sleep.js';
56

67
const logger = new MockLogger();
78

@@ -85,7 +86,7 @@ describe('shellMessageTool', () => {
8586
const instanceId = getInstanceId(startResult);
8687

8788
// Wait a moment for process to complete
88-
await new Promise((resolve) => setTimeout(resolve, 150));
89+
await sleep(150);
8990

9091
const result = await shellMessageTool.execute(
9192
{
@@ -123,7 +124,7 @@ describe('shellMessageTool', () => {
123124
);
124125
expect(result.signaled).toBe(true);
125126

126-
await new Promise((resolve) => setTimeout(resolve, 50));
127+
await sleep(50);
127128

128129
const result2 = await shellMessageTool.execute(
129130
{
@@ -187,7 +188,7 @@ describe('shellMessageTool', () => {
187188
{ logger },
188189
);
189190

190-
await new Promise((resolve) => setTimeout(resolve, 50));
191+
await sleep(50);
191192

192193
// Check process state after signal
193194
const checkResult = await shellMessageTool.execute(

src/tools/system/shellMessage.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Tool } from '../../core/types.js';
22
import { z } from 'zod';
33
import { zodToJsonSchema } from 'zod-to-json-schema';
44
import { processStates } from './shellStart.js';
5+
import { sleep } from '../../utils/sleep.js';
56

67
// Define NodeJS signals as an enum
78
export enum NodeSignals {
@@ -114,7 +115,7 @@ export const shellMessageTool: Tool<Parameters, ReturnType> = {
114115
}
115116

116117
// Wait a brief moment for output to be processed
117-
await new Promise((resolve) => setTimeout(resolve, 100));
118+
await sleep(100);
118119

119120
// Get accumulated output
120121
const stdout = processState.stdout.join('');

src/tools/system/shellStart.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
22
import { processStates, shellStartTool } from './shellStart.js';
33
import { MockLogger } from '../../utils/mockLogger.js';
4+
import { sleep } from '../../utils/sleep.js';
45

56
const logger = new MockLogger();
67

@@ -126,7 +127,7 @@ describe('shellStartTool', () => {
126127
processState.process.stdin.end();
127128

128129
// Wait for output
129-
await new Promise((resolve) => setTimeout(resolve, 200));
130+
await sleep(200);
130131

131132
// Check stdout in processState
132133
expect(processState.stdout.join('')).toContain('test');

src/tools/system/sleep.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { z } from 'zod';
2+
import { zodToJsonSchema } from 'zod-to-json-schema';
3+
import { Tool } from '../../core/types.js';
4+
import { sleep } from '../../utils/sleep.js';
5+
6+
const MAX_SLEEP_SECONDS = 3600; // 1 hour
7+
8+
const parametersSchema = z.object({
9+
seconds: z
10+
.number()
11+
.min(0)
12+
.max(MAX_SLEEP_SECONDS)
13+
.describe('Number of seconds to sleep (max 1 hour)'),
14+
});
15+
16+
const returnsSchema = z.object({
17+
sleptFor: z.number().describe('Actual number of seconds slept'),
18+
});
19+
20+
export const sleepTool: Tool = {
21+
name: 'sleep',
22+
description:
23+
'Pauses execution for the specified number of seconds, useful when waiting for async tools to make progress before checking on them',
24+
parameters: zodToJsonSchema(parametersSchema),
25+
returns: zodToJsonSchema(returnsSchema),
26+
async execute(params) {
27+
const { seconds } = parametersSchema.parse(params);
28+
29+
await sleep(seconds * 1000);
30+
31+
return returnsSchema.parse({
32+
sleptFor: seconds,
33+
});
34+
},
35+
logParameters({ seconds }, { logger }) {
36+
logger.info( `sleeping for ${seconds} seconds` );
37+
},
38+
logReturns() {
39+
},
40+
};

src/utils/sleep.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const sleep = (milliseconds: number) =>
2+
new Promise((resolve) => setTimeout(resolve, milliseconds));

tests/tools/system/sleep.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { sleepTool } from '../../../src/tools/system/sleep';
3+
4+
describe('sleep tool', () => {
5+
beforeEach(() => {
6+
vi.useFakeTimers();
7+
});
8+
9+
it('should sleep for the specified duration', async () => {
10+
const startTime = Date.now();
11+
const sleepPromise = sleepTool.execute({ seconds: 2 });
12+
13+
await vi.advanceTimersByTimeAsync(2000);
14+
const result = await sleepPromise;
15+
16+
expect(result).toEqual({ sleptFor: 2 });
17+
});
18+
19+
it('should reject negative sleep duration', async () => {
20+
await expect(sleepTool.execute({ seconds: -1 })).rejects.toThrow();
21+
});
22+
23+
it('should reject sleep duration over 1 hour', async () => {
24+
await expect(sleepTool.execute({ seconds: 3601 })).rejects.toThrow();
25+
});
26+
27+
it('should format log parameters correctly', () => {
28+
expect(sleepTool.logParameters({ seconds: 5 })).toBe('sleeping for 5 seconds');
29+
});
30+
31+
it('should format log returns correctly', () => {
32+
expect(sleepTool.logReturns({ sleptFor: 5 })).toBe('');
33+
});
34+
});

0 commit comments

Comments
 (0)