diff --git a/README.md b/README.md index 617e507..c7ef9eb 100755 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ export OPENROUTER_API_KEY=... # or VENICE_API_KEY / ANTHROPIC_API_KEY / OPEN export XAI_API_KEY=... # Grok Build (grok-build-0.1) — xAI's coding model, native tool-calling ``` +Slow local agents can be given more room with `T3MP3ST_LOCAL_AGENT_TIMEOUT_MS` +for each CLI call, `T3MP3ST_TASK_TIMEOUT_MS` for mission tasks, and +`T3MP3ST_GENERAL_TIMEOUT_MS` for planning requests. Values are milliseconds. + Or run it **fully offline** on your own model — no key, no cloud. Defaults to Ollama; point it at any OpenAI-compatible server (LM Studio, vLLM, llama.cpp): ```bash diff --git a/src/__tests__/local-agent-tool-calling.test.ts b/src/__tests__/local-agent-tool-calling.test.ts index a5791d2..45a3fb4 100644 --- a/src/__tests__/local-agent-tool-calling.test.ts +++ b/src/__tests__/local-agent-tool-calling.test.ts @@ -71,6 +71,15 @@ describe('parseTextToolCalls — happy path + drift tolerance', () => { }); describe('local-agent backbone surfaces toolCalls (keyless-path fix)', () => { + it('describes the current ACTION CONTRACT to the local agent', async () => { + cli.mockResolvedValueOnce('Final debrief.'); + await localBackbone().chatWithTools([{ role: 'user', content: 'scan' }], TOOLS as never); + const prompt = String(cli.mock.calls.at(-1)?.[1] || ''); + expect(prompt).toContain('ACTION CONTRACT'); + expect(prompt).toContain('nmap_scan'); + expect(prompt).toContain('port scan a host'); + }); + it('returns toolCalls + finishReason=tool_calls when the agent requests a tool', async () => { cli.mockResolvedValueOnce('```json\n{"tool_calls":[{"name":"nmap_scan","arguments":{"target":"x"}}]}\n```'); const res = await localBackbone().chatWithTools([{ role: 'user', content: 'scan' }], TOOLS as never); @@ -83,6 +92,19 @@ describe('local-agent backbone surfaces toolCalls (keyless-path fix)', () => { expect(res.toolCalls).toBeUndefined(); expect(res.finishReason).toBe('stop'); }); + + it('does not force the old short timeout when no local-agent timeout is configured', async () => { + cli.mockResolvedValueOnce('done'); + await localBackbone().chat([{ role: 'user', content: 'hello' }]); + expect(cli.mock.calls.at(-1)?.[2]).toMatchObject({ timeoutMs: undefined }); + }); + + it('passes an explicit local-agent timeout through to the CLI runner', async () => { + cli.mockResolvedValueOnce('done'); + await new LLMBackbone({ provider: 'local-agent', model: 'codex', timeout: 900000 } as never) + .chat([{ role: 'user', content: 'hello' }]); + expect(cli.mock.calls.at(-1)?.[2]).toMatchObject({ timeoutMs: 900000 }); + }); }); describe('codex backbone surfaces toolCalls (guards the CodexAdapter half of the fix)', () => {