feat(agent-loop): run safe tool calls in parallel
This commit is contained in:
@@ -11,6 +11,8 @@ Each tool is registered independently; a product that wants only one disables th
|
||||
| `web_search` | `query` (string) | Discovery. Returns an optional answer plus source URLs. `max_results` is **not** model-facing — the tool sets the bound (the `searchMaxResults` config, default 8) and passes it to the seam. |
|
||||
| `web_fetch` | `url` (string) | Retrieves a specific URL. HTML bodies are rendered to markdown-ish text; text bodies pass through. A non-2xx status is reported, not an error. The tool-call timeout is deployment policy (`dsh-timeout-policy`), not a model argument. |
|
||||
|
||||
Both tools declare `isConcurrencySafe: () => true` — they are read-only (fetch a provider/URL, return content, mutate no parent-agent state), so the agent loop may run sibling web calls in parallel.
|
||||
|
||||
## Config
|
||||
|
||||
| Key | Default | Meaning |
|
||||
|
||||
@@ -99,6 +99,9 @@ export function applyWebFetchTool(ctx: Context, timeoutMs: number): void {
|
||||
url: { type: 'string', required: true, description: 'The HTTP(S) URL to fetch.' },
|
||||
},
|
||||
timeoutMs,
|
||||
// Read-only: fetching a URL returns content and mutates no parent-agent
|
||||
// state — safe to run concurrently with sibling calls.
|
||||
isConcurrencySafe: () => true,
|
||||
async execute(args, exec): Promise<ContentBlock[]> {
|
||||
const input = parseFetchArgs(args)
|
||||
const result = await ctx.web.fetch(
|
||||
|
||||
@@ -109,6 +109,9 @@ export function applyWebSearchTool(ctx: Context, maxResults: number, timeoutMs:
|
||||
query: { type: 'string', required: true, description: 'The search query.' },
|
||||
},
|
||||
timeoutMs,
|
||||
// Read-only: a search hits the provider and returns content, mutating no
|
||||
// parent-agent state — safe to run concurrently with sibling calls.
|
||||
isConcurrencySafe: () => true,
|
||||
async execute(args, exec): Promise<ContentBlock[]> {
|
||||
const input = parseSearchArgs(args)
|
||||
const result = await ctx.web.search(
|
||||
|
||||
@@ -166,6 +166,10 @@ describe('tool-web registration', () => {
|
||||
const names = ctx.tools.schemas().map(s => s.name)
|
||||
expect(names).toContain('web_search')
|
||||
expect(names).toContain('web_fetch')
|
||||
expect(ctx.tools.executionMode({ callId: CallId('search-safe'), name: 'web_search', arguments: { query: 'q' } }))
|
||||
.toEqual({ kind: 'parallel' })
|
||||
expect(ctx.tools.executionMode({ callId: CallId('fetch-safe'), name: 'web_fetch', arguments: { url: 'https://a.test' } }))
|
||||
.toEqual({ kind: 'parallel' })
|
||||
await fiber.dispose()
|
||||
expect(ctx.tools.schemas().map(s => s.name)).not.toContain('web_search')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user