From b66e93832adab596bb99d93284c3387b31482457 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 6 Jul 2026 20:17:42 +0200 Subject: [PATCH 01/10] feat: Add eve first party eve instrumentation --- e2e/config/pr-comment-scenarios.json | 6 + .../__cassettes__/eve-v0-20-0.cassette.json | 1274 +++++++++++++++++ .../eve-instrumentation.span-tree.json | 281 ++++ .../eve-instrumentation.span-tree.txt | 253 ++++ .../eve-instrumentation/agent/agent.ts | 13 + .../agent/hooks/braintrust.ts | 4 + .../eve-instrumentation/agent/instructions.md | 10 + .../agent/instrumentation.ts | 10 + .../eve-instrumentation/agent/tools/read.ts | 18 + .../eve-instrumentation/agent/tools/search.ts | 16 + .../eve-instrumentation/cassette-filter.mjs | 21 + .../eve-instrumentation/package.json | 10 + .../eve-instrumentation/pnpm-lock.yaml | 737 ++++++++++ .../eve-instrumentation/scenario.test.ts | 116 ++ e2e/scenarios/eve-instrumentation/scenario.ts | 208 +++ .../eve-instrumentation/tsconfig.json | 9 + js/src/exports.ts | 1 + js/src/instrumentation/index.ts | 1 + .../plugins/eve-plugin.test.ts | 344 +++++ js/src/instrumentation/plugins/eve-plugin.ts | 953 ++++++++++++ js/src/vendor-sdk-types/eve.ts | 257 ++++ 21 files changed, 4542 insertions(+) create mode 100644 e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json create mode 100644 e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json create mode 100644 e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt create mode 100644 e2e/scenarios/eve-instrumentation/agent/agent.ts create mode 100644 e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts create mode 100644 e2e/scenarios/eve-instrumentation/agent/instructions.md create mode 100644 e2e/scenarios/eve-instrumentation/agent/instrumentation.ts create mode 100644 e2e/scenarios/eve-instrumentation/agent/tools/read.ts create mode 100644 e2e/scenarios/eve-instrumentation/agent/tools/search.ts create mode 100644 e2e/scenarios/eve-instrumentation/cassette-filter.mjs create mode 100644 e2e/scenarios/eve-instrumentation/package.json create mode 100644 e2e/scenarios/eve-instrumentation/pnpm-lock.yaml create mode 100644 e2e/scenarios/eve-instrumentation/scenario.test.ts create mode 100644 e2e/scenarios/eve-instrumentation/scenario.ts create mode 100644 e2e/scenarios/eve-instrumentation/tsconfig.json create mode 100644 js/src/instrumentation/plugins/eve-plugin.test.ts create mode 100644 js/src/instrumentation/plugins/eve-plugin.ts create mode 100644 js/src/vendor-sdk-types/eve.ts diff --git a/e2e/config/pr-comment-scenarios.json b/e2e/config/pr-comment-scenarios.json index 30ada879e..407d03fc2 100644 --- a/e2e/config/pr-comment-scenarios.json +++ b/e2e/config/pr-comment-scenarios.json @@ -224,6 +224,12 @@ { "variantKey": "flue-v1-0-0-beta-3", "label": "v1.0.0-beta.3" } ] }, + { + "scenarioDirName": "eve-instrumentation", + "label": "Eve Instrumentation", + "metadataScenario": "eve-instrumentation", + "variants": [{ "variantKey": "eve-v0-20-0", "label": "v0.20.0" }] + }, { "scenarioDirName": "github-copilot-instrumentation", "label": "GitHub Copilot SDK Instrumentation", diff --git a/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json b/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json new file mode 100644 index 000000000..3e0f496b7 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json @@ -0,0 +1,1274 @@ +{ + "entries": [ + { + "callIndex": 0, + "id": "63740c316833eadb", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-06T12:42:40.161Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the search tool exactly once with the full user message as the query.\n2. After the search result is available, call the read tool exactly once with the\n URL returned by search.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the search title, URL, and read\n excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Reads a deterministic Eve documentation page fixture.", + "name": "read", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2bed894819f941441a0e61d48b8\",\"object\":\"response\",\"created_at\":1783341758,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2bed894819f941441a0e61d48b8\",\"object\":\"response\",\"created_at\":1783341758,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_OJGkAZ5ouXaH7NjuUH2mSbk6\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"XnL4BuCUjYo2Ga\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"query\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"l6rkWWYxmHg\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"GymIr2w2XZVpA\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"OwgJorNkY8F7z\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"zI4Q0bRZ70XL\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"1mvqmuEoZG\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"7LdGHqhYJm5\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"SUjuR9onJ7HP\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"9vSg1jlhKVxvd4\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"lkED6zGdV426rzv\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"7ZbUxm5q3VLOlzU\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"fjhDwnd\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"rtm0Kzc5YO8U7T\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_OJGkAZ5ouXaH7NjuUH2mSbk6\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":18}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2bed894819f941441a0e61d48b8\",\"object\":\"response\",\"created_at\":1783341758,\"status\":\"completed\",\"background\":false,\"completed_at\":1783341760,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_OJGkAZ5ouXaH7NjuUH2mSbk6\",\"name\":\"search\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6564,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":26,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6590},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a16eb0c61b905b89-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Mon, 06 Jul 2026 12:42:39 GMT", + "openai-processing-ms": "301", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992794", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "c268729b-cdae-494b-a9f6-d154a9096847" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 1, + "id": "6eaa461531ba23e9", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-06T12:42:41.865Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the search tool exactly once with the full user message as the query.\n2. After the search result is available, call the read tool exactly once with the\n URL returned by search.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the search title, URL, and read\n excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea", + "type": "item_reference" + }, + { + "call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6", + "output": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Reads a deterministic Eve documentation page fixture.", + "name": "read", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c082ac819fa0b53db4088f4b4b\",\"object\":\"response\",\"created_at\":1783341760,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c082ac819fa0b53db4088f4b4b\",\"object\":\"response\",\"created_at\":1783341760,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_kZ7AzZ9mCqataCzrSSLxRkFh\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"hCZj3eURcxA5u7\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"url\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"YTahm2y9wc2a2\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"O9KdvSTHznEA4\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"https\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"cZXggUplvDp\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"://\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"W1IPVLgHRdfnK\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"eve\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"IlAAdBPloI7ZI\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\".dev\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"vuTEb7koKwkU\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/docs\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"3tkcCXVFaVS\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/g\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"Fnb0kAAbyEFxmo\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"uid\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"F53YeZTZPgt2h\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"es\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"wc5Z1NagkfGlho\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/in\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"YVqWvIHZvcnXv\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"strument\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"9e6Bnngc\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"ation\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"5nPU7ASnvsR\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"tYrx4WFsbNsUju\",\"output_index\":0,\"sequence_number\":17}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_kZ7AzZ9mCqataCzrSSLxRkFh\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":19}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c082ac819fa0b53db4088f4b4b\",\"object\":\"response\",\"created_at\":1783341760,\"status\":\"completed\",\"background\":false,\"completed_at\":1783341761,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_kZ7AzZ9mCqataCzrSSLxRkFh\",\"name\":\"read\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6634,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6661},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a16eb0d248325b89-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Mon, 06 Jul 2026 12:42:41 GMT", + "openai-processing-ms": "505", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992725", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "2a2e63ed-abb5-4d63-9732-9e1203d43cd5" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 2, + "id": "4b05a20680820f51", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-06T12:42:43.115Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the search tool exactly once with the full user message as the query.\n2. After the search result is available, call the read tool exactly once with the\n URL returned by search.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the search title, URL, and read\n excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea", + "type": "item_reference" + }, + { + "call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6", + "output": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + }, + { + "id": "fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46", + "type": "item_reference" + }, + { + "call_id": "call_kZ7AzZ9mCqataCzrSSLxRkFh", + "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Reads a deterministic Eve documentation page fixture.", + "name": "read", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c24358819fb60d53217b74c525\",\"object\":\"response\",\"created_at\":1783341762,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c24358819fb60d53217b74c525\",\"object\":\"response\",\"created_at\":1783341762,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Final\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ANAxDak0zuB\",\"output_index\":0,\"sequence_number\":4}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" answer\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"A7UjBuGVk\",\"output_index\":0,\"sequence_number\":5}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" from\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"yeX5bNWtb13\",\"output_index\":0,\"sequence_number\":6}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"LZJe1rtmYvV\",\"output_index\":0,\"sequence_number\":7}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ubVMHAlNIlruqHa\",\"output_index\":0,\"sequence_number\":8}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"jNlbYXRxMdDm\",\"output_index\":0,\"sequence_number\":9}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":10}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"r53TzX4FOBazqH\",\"output_index\":0,\"sequence_number\":11}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"DO7NjjcMxI\",\"output_index\":0,\"sequence_number\":12}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ICOb1qKdbbUrP\",\"output_index\":0,\"sequence_number\":13}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"SaQSy8Bew6PYA\",\"output_index\":0,\"sequence_number\":14}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zMGbfUgBaedB\",\"output_index\":0,\"sequence_number\":15}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"PRFSTu78jMq\",\"output_index\":0,\"sequence_number\":16}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"TZIiRl9V1OzAAJ\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ztrmEJnKeliLB\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"vJXlAF5E6no9TP\",\"output_index\":0,\"sequence_number\":19}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"2jUQoFtlTAB9n\",\"output_index\":0,\"sequence_number\":20}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"Q0nLWTe1\",\"output_index\":0,\"sequence_number\":21}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"xUWKmOn2oyt\",\"output_index\":0,\"sequence_number\":22}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zNi8pD4u1Ja7L1\",\"output_index\":0,\"sequence_number\":23}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"1Zebv58iK37e\",\"output_index\":0,\"sequence_number\":24}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" hooks\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"S97nFDYp7A\",\"output_index\":0,\"sequence_number\":25}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" expose\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ZmTvzTqky\",\"output_index\":0,\"sequence_number\":26}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" runtime\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"fB1yh7Uw\",\"output_index\":0,\"sequence_number\":27}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" stream\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zU1vbebdy\",\"output_index\":0,\"sequence_number\":28}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" events\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"gYKd55Lzb\",\"output_index\":0,\"sequence_number\":29}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" that\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"3EZn0CVOJaT\",\"output_index\":0,\"sequence_number\":30}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Brain\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"atGReXeapP\",\"output_index\":0,\"sequence_number\":31}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"trust\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zWKPFGInVjG\",\"output_index\":0,\"sequence_number\":32}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" maps\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"EeGFqBdfAME\",\"output_index\":0,\"sequence_number\":33}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" into\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"iabXIGBKBpJ\",\"output_index\":0,\"sequence_number\":34}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" a\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"5CXblMcSRN8S5K\",\"output_index\":0,\"sequence_number\":35}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" flat\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"P6u4aXRbe8f\",\"output_index\":0,\"sequence_number\":36}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" turn\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"9P4dWv48mQN\",\"output_index\":0,\"sequence_number\":37}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" trace\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"cZVEaJUhkd\",\"output_index\":0,\"sequence_number\":38}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"qL2yxmkMIJgYbnb\",\"output_index\":0,\"sequence_number\":39}", + "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":40,\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"}", + "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"},\"sequence_number\":41}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":42}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c24358819fb60d53217b74c525\",\"object\":\"response\",\"created_at\":1783341762,\"status\":\"completed\",\"background\":false,\"completed_at\":1783341762,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6716,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":40,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6756},\"user\":null,\"metadata\":{}},\"sequence_number\":43}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a16eb0dd6acb5b89-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Mon, 06 Jul 2026 12:42:42 GMT", + "openai-processing-ms": "277", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992644", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "526d60e7-9511-4966-9fc6-ec90737f89a8" + }, + "status": 200, + "statusText": "OK" + } + } + ], + "meta": { + "createdAt": "2026-07-06T10:11:31.744Z" + } +} diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json new file mode 100644 index 000000000..06aad68d8 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -0,0 +1,281 @@ +{ + "span_tree": [ + { + "name": "eve.turn", + "type": "task", + "children": [ + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], + "output": { + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + } + }, + "metadata": { + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.finish_reason": "tool-calls", + "eve.model.id": "openai/gpt-5.4-mini", + "eve.session.id": "", + "eve.step.index": 0, + "eve.turn.id": "", + "eve.turn.sequence": 0, + "model": "gpt-5.4-mini", + "provider": "openai" + }, + "metrics": { + "completion_tokens": 26, + "prompt_cached_tokens": 0, + "prompt_tokens": 6564, + "tokens": 6590 + } + }, + { + "name": "search", + "type": "tool", + "children": [], + "input": { + "query": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "output": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "metadata": { + "eve.action.status": "completed", + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.session.id": "", + "eve.step.index": 0, + "eve.tool.call_id": "", + "eve.tool.name": "search", + "eve.turn.id": "", + "eve.turn.sequence": 0 + } + }, + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + }, + { + "content": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "name": "search", + "role": "tool", + "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" + } + ], + "output": { + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "name": "read" + }, + "id": "", + "type": "function" + } + ] + } + }, + "metadata": { + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.finish_reason": "tool-calls", + "eve.model.id": "openai/gpt-5.4-mini", + "eve.session.id": "", + "eve.step.index": 1, + "eve.turn.id": "", + "eve.turn.sequence": 0, + "model": "gpt-5.4-mini", + "provider": "openai" + }, + "metrics": { + "completion_tokens": 27, + "prompt_cached_tokens": 0, + "prompt_tokens": 6634, + "tokens": 6661 + } + }, + { + "name": "read", + "type": "tool", + "children": [], + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "output": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "metadata": { + "eve.action.status": "completed", + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.session.id": "", + "eve.step.index": 1, + "eve.tool.call_id": "", + "eve.tool.name": "read", + "eve.turn.id": "", + "eve.turn.sequence": 0 + } + }, + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + }, + { + "content": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "name": "search", + "role": "tool", + "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" + }, + { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "name": "read" + }, + "id": "", + "type": "function" + } + ] + }, + { + "content": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "name": "read", + "role": "tool", + "tool_call_id": "call_kZ7AzZ9mCqataCzrSSLxRkFh" + } + ], + "output": { + "finish_reason": "stop", + "message": { + "content": "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "role": "assistant" + } + }, + "metadata": { + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.finish_reason": "stop", + "eve.model.id": "openai/gpt-5.4-mini", + "eve.session.id": "", + "eve.step.index": 2, + "eve.turn.id": "", + "eve.turn.sequence": 0, + "model": "gpt-5.4-mini", + "provider": "openai" + }, + "metrics": { + "completion_tokens": 40, + "prompt_cached_tokens": 0, + "prompt_tokens": 6716, + "tokens": 6756 + } + } + ], + "input": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], + "output": "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "metadata": { + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.model.id": "openai/gpt-5.4-mini", + "eve.session.id": "", + "eve.turn.id": "", + "eve.turn.sequence": 0, + "model": "gpt-5.4-mini", + "provider": "openai" + }, + "metrics": { + "completion_tokens": 93, + "prompt_cached_tokens": 0, + "prompt_tokens": 19914, + "tokens": 20007 + } + } + ] +} diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt new file mode 100644 index 000000000..f6bfa4a24 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -0,0 +1,253 @@ +span_tree: +└── eve.turn [task] + input: [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ] + output: "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace." + metadata: { + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.model.id": "openai/gpt-5.4-mini", + "eve.session.id": "", + "eve.turn.id": "", + "eve.turn.sequence": 0, + "model": "gpt-5.4-mini", + "provider": "openai" + } + metrics: { + "completion_tokens": 93, + "prompt_cached_tokens": 0, + "prompt_tokens": 19914, + "tokens": 20007 + } + ├── eve.step [llm] + │ input: [ + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ } + │ ] + │ output: { + │ "message": { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ "name": "search" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ } + │ } + │ metadata: { + │ "eve.agent.name": "eve-instrumentation-scenario", + │ "eve.channel.kind": "http", + │ "eve.finish_reason": "tool-calls", + │ "eve.model.id": "openai/gpt-5.4-mini", + │ "eve.session.id": "", + │ "eve.step.index": 0, + │ "eve.turn.id": "", + │ "eve.turn.sequence": 0, + │ "model": "gpt-5.4-mini", + │ "provider": "openai" + │ } + │ metrics: { + │ "completion_tokens": 26, + │ "prompt_cached_tokens": 0, + │ "prompt_tokens": 6564, + │ "tokens": 6590 + │ } + ├── search [tool] + │ input: { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario" + │ } + │ output: { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ metadata: { + │ "eve.action.status": "completed", + │ "eve.agent.name": "eve-instrumentation-scenario", + │ "eve.channel.kind": "http", + │ "eve.session.id": "", + │ "eve.step.index": 0, + │ "eve.tool.call_id": "", + │ "eve.tool.name": "search", + │ "eve.turn.id": "", + │ "eve.turn.sequence": 0 + │ } + ├── eve.step [llm] + │ input: [ + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ "name": "search" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ }, + │ { + │ "content": { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ }, + │ "name": "search", + │ "role": "tool", + │ "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" + │ } + │ ] + │ output: { + │ "message": { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + │ "name": "read" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ } + │ } + │ metadata: { + │ "eve.agent.name": "eve-instrumentation-scenario", + │ "eve.channel.kind": "http", + │ "eve.finish_reason": "tool-calls", + │ "eve.model.id": "openai/gpt-5.4-mini", + │ "eve.session.id": "", + │ "eve.step.index": 1, + │ "eve.turn.id": "", + │ "eve.turn.sequence": 0, + │ "model": "gpt-5.4-mini", + │ "provider": "openai" + │ } + │ metrics: { + │ "completion_tokens": 27, + │ "prompt_cached_tokens": 0, + │ "prompt_tokens": 6634, + │ "tokens": 6661 + │ } + ├── read [tool] + │ input: { + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ output: { + │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + │ "section": "Runtime context", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ metadata: { + │ "eve.action.status": "completed", + │ "eve.agent.name": "eve-instrumentation-scenario", + │ "eve.channel.kind": "http", + │ "eve.session.id": "", + │ "eve.step.index": 1, + │ "eve.tool.call_id": "", + │ "eve.tool.name": "read", + │ "eve.turn.id": "", + │ "eve.turn.sequence": 0 + │ } + └── eve.step [llm] + input: [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + }, + { + "content": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "name": "search", + "role": "tool", + "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" + }, + { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "name": "read" + }, + "id": "", + "type": "function" + } + ] + }, + { + "content": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "name": "read", + "role": "tool", + "tool_call_id": "call_kZ7AzZ9mCqataCzrSSLxRkFh" + } + ] + output: { + "finish_reason": "stop", + "message": { + "content": "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "role": "assistant" + } + } + metadata: { + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.finish_reason": "stop", + "eve.model.id": "openai/gpt-5.4-mini", + "eve.session.id": "", + "eve.step.index": 2, + "eve.turn.id": "", + "eve.turn.sequence": 0, + "model": "gpt-5.4-mini", + "provider": "openai" + } + metrics: { + "completion_tokens": 40, + "prompt_cached_tokens": 0, + "prompt_tokens": 6716, + "tokens": 6756 + } diff --git a/e2e/scenarios/eve-instrumentation/agent/agent.ts b/e2e/scenarios/eve-instrumentation/agent/agent.ts new file mode 100644 index 000000000..467390630 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/agent.ts @@ -0,0 +1,13 @@ +import { defineAgent } from "eve"; +import { createOpenAI } from "@ai-sdk/openai"; + +const openai = createOpenAI({ + ...(process.env.OPENAI_BASE_URL + ? { baseURL: process.env.OPENAI_BASE_URL } + : {}), +}); + +export default defineAgent({ + model: openai("gpt-5.4-mini"), + modelContextWindowTokens: 8_192, +}); diff --git a/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts b/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts new file mode 100644 index 000000000..4b0d8c56e --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts @@ -0,0 +1,4 @@ +import { braintrustEveHook } from "braintrust"; +import { defineHook } from "eve/hooks"; + +export default defineHook(braintrustEveHook()); diff --git a/e2e/scenarios/eve-instrumentation/agent/instructions.md b/e2e/scenarios/eve-instrumentation/agent/instructions.md new file mode 100644 index 000000000..a4715d143 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/instructions.md @@ -0,0 +1,10 @@ +You are a deterministic fixture agent for Braintrust Eve instrumentation tests. + +For every user task: + +1. Call the search tool exactly once with the full user message as the query. +2. After the search result is available, call the read tool exactly once with the + URL returned by search. +3. After the read result is available, answer with a single sentence that starts + with "Final answer from read:" and includes the search title, URL, and read + excerpt. diff --git a/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts b/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts new file mode 100644 index 000000000..297f3555f --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts @@ -0,0 +1,10 @@ +import { initLogger } from "braintrust"; +import { defineInstrumentation } from "eve/instrumentation"; + +export default defineInstrumentation({ + setup: ({ agentName }) => { + initLogger({ + projectName: process.env.BRAINTRUST_E2E_PROJECT_NAME || agentName, + }); + }, +}); diff --git a/e2e/scenarios/eve-instrumentation/agent/tools/read.ts b/e2e/scenarios/eve-instrumentation/agent/tools/read.ts new file mode 100644 index 000000000..66e0e261b --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/tools/read.ts @@ -0,0 +1,18 @@ +import { defineTool } from "eve/tools"; +import { z } from "zod"; + +export default defineTool({ + description: "Reads a deterministic Eve documentation page fixture.", + inputSchema: z.object({ + url: z.string(), + }), + async execute({ url }) { + return { + excerpt: + "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + section: "Runtime context", + title: "Eve instrumentation", + url, + }; + }, +}); diff --git a/e2e/scenarios/eve-instrumentation/agent/tools/search.ts b/e2e/scenarios/eve-instrumentation/agent/tools/search.ts new file mode 100644 index 000000000..dc95b8ea1 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/tools/search.ts @@ -0,0 +1,16 @@ +import { defineTool } from "eve/tools"; +import { z } from "zod"; + +export default defineTool({ + description: "Searches the Eve instrumentation documentation fixture.", + inputSchema: z.object({ + query: z.string(), + }), + async execute({ query }) { + return { + query, + title: "Eve instrumentation", + url: "https://eve.dev/docs/guides/instrumentation", + }; + }, +}); diff --git a/e2e/scenarios/eve-instrumentation/cassette-filter.mjs b/e2e/scenarios/eve-instrumentation/cassette-filter.mjs new file mode 100644 index 000000000..f73d1a5ef --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/cassette-filter.mjs @@ -0,0 +1,21 @@ +// @ts-check +export { filter } from "../ai-sdk-instrumentation/cassette-filter.mjs"; + +/** @type {import("@braintrust/seinfeld").RedactionSpec} */ +export const redact = [ + "paranoid", + { + redactResponse(response) { + return { + ...response, + headers: Object.fromEntries( + Object.entries(response.headers).filter( + ([key]) => + key.toLowerCase() !== "openai-organization" && + key.toLowerCase() !== "openai-project", + ), + ), + }; + }, + }, +]; diff --git a/e2e/scenarios/eve-instrumentation/package.json b/e2e/scenarios/eve-instrumentation/package.json new file mode 100644 index 000000000..a24533ef9 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/package.json @@ -0,0 +1,10 @@ +{ + "name": "eve-instrumentation-scenario", + "private": true, + "type": "module", + "dependencies": { + "@ai-sdk/openai": "4.0.0-beta.44", + "eve": "0.20.0", + "zod": "4.3.6" + } +} diff --git a/e2e/scenarios/eve-instrumentation/pnpm-lock.yaml b/e2e/scenarios/eve-instrumentation/pnpm-lock.yaml new file mode 100644 index 000000000..a229714ae --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/pnpm-lock.yaml @@ -0,0 +1,737 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@ai-sdk/openai': + specifier: 4.0.0-beta.44 + version: 4.0.0-beta.44(zod@4.3.6) + eve: + specifier: 0.20.0 + version: 0.20.0(ai@7.0.15(zod@4.3.6)) + zod: + specifier: 4.3.6 + version: 4.3.6 + +packages: + + '@ai-sdk/gateway@4.0.12': + resolution: {integrity: sha512-Y7Fy8xJwPz7ZC0DhSQG3HIVk+drup42hrIj6yqKlib3CxwiR0F7nYyUI8+kPrEtbZEoyKoRstvT4/o0HEyFBHA==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/openai@4.0.0-beta.44': + resolution: {integrity: sha512-kXdGA6vyh9SwL/B11UhODjbw0koFkDA/yD7hacBxMHSZx6l3T9hZLECEwOkn6OZDrQR+vno5Ka/pUyXrG82BkQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@5.0.0-beta.30': + resolution: {integrity: sha512-X3AxTsFJZr9Mw32SncWJA4/ShU8NA/XjhoallCYdcdQZ2gZZ/cIDyJJ6pNh1aBhicRNHv2ndgI6w/H2NGNIjOA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@5.0.5': + resolution: {integrity: sha512-oI0t3dvCoqWNV1I8o1Rybi2DXDvHES5r/TrwtJW90tuFLVepgJlftPxrcjh8vaSvjqC2diTuA2vXyjKAyHJm4A==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@4.0.0-beta.14': + resolution: {integrity: sha512-SMijtjVHs38n0dMkTcNuGrnStiF76OhAqS0R+ZX4iXUnuPPyTxQoVcF8Xuf2AoBB5rqndTId5FVT05bgqD5KsA==} + engines: {node: '>=18'} + + '@ai-sdk/provider@4.0.2': + resolution: {integrity: sha512-pfPoy9J1B1xV7cqJ8MYHOsDYrMv5tR3+EMNfI249OhkD2uRakvav3Fo7XpD2luuN/YNCBY7KfEQc7vEV7KEtyw==} + engines: {node: '>=22'} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.138.0': + resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==} + + '@rolldown/binding-android-arm64@1.1.4': + resolution: {integrity: sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.4': + resolution: {integrity: sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.4': + resolution: {integrity: sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.4': + resolution: {integrity: sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + resolution: {integrity: sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.4': + resolution: {integrity: sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.4': + resolution: {integrity: sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.4': + resolution: {integrity: sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.4': + resolution: {integrity: sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.4': + resolution: {integrity: sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.4': + resolution: {integrity: sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.4': + resolution: {integrity: sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.4': + resolution: {integrity: sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.4': + resolution: {integrity: sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.4': + resolution: {integrity: sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} + engines: {node: '>= 20'} + + '@workflow/serde@4.1.0': + resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} + + ai@7.0.15: + resolution: {integrity: sha512-7406MUy9O5sIhwOgxEWuoj+td3XUGgG96SBt6pmBU4t4sQ2fpnDx5UnHaXT2HUTXl5GorbWz/MfCrSPRz0QJqw==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + crossws@0.4.9: + resolution: {integrity: sha512-iWx+1OMSG2aOHpjyf9AESOzkwsVdS49cXM9dVrI2PDhxU5l2RIWE/KG56gk4BbAnsMoycvniJ9OnOxO9LRzHVA==} + peerDependencies: + srvx: '>=0.11.5' + peerDependenciesMeta: + srvx: + optional: true + + db0@0.3.4: + resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==} + peerDependencies: + '@electric-sql/pglite': '*' + '@libsql/client': '*' + better-sqlite3: '*' + drizzle-orm: '*' + mysql2: '*' + sqlite3: '*' + peerDependenciesMeta: + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + better-sqlite3: + optional: true + drizzle-orm: + optional: true + mysql2: + optional: true + sqlite3: + optional: true + + env-runner@0.1.16: + resolution: {integrity: sha512-2LRJM4P2KLX6J83QZZrMqvgCDt/D5ea7wPcI3yYiy5cG/9rX5QwdwZFx0D7ktWnjdRyZxYjttGGorb5nFqb1CA==} + hasBin: true + peerDependencies: + '@netlify/runtime': ^4.1.23 + '@vercel/queue': '>=0.2.0' + miniflare: ^4.20260515.0 + wrangler: ^4.0.0 + peerDependenciesMeta: + '@netlify/runtime': + optional: true + '@vercel/queue': + optional: true + miniflare: + optional: true + wrangler: + optional: true + + eve@0.20.0: + resolution: {integrity: sha512-O218jbwGCjg0ygTcBKuIy+c4UqL84/Zyala9qOAEhiMEVUpllUU6VZriQcnQJkkQscpOiOikF0rcWgUABBVrrw==} + engines: {node: '>=24'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + ai: ^7.0.0 + braintrust: ^3.0.0 + just-bash: ^3.0.0 + microsandbox: ^0.5.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + braintrust: + optional: true + just-bash: + optional: true + microsandbox: + optional: true + + eventsource-parser@3.1.0: + resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} + engines: {node: '>=18.0.0'} + + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} + + h3@2.0.1-rc.22: + resolution: {integrity: sha512-Esv0DMIuPkCTSWCA0vO73vcTqwzH1wjSrAO1TXNu/K3up1sZHa9EKMapbmxCDYBeymC3fVTk4qxp7ogQWQ+KgA==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + + httpxy@0.5.4: + resolution: {integrity: sha512-URfeibL0kTH6VuIxxaJDXWQWEk8fKr+9L8MGv6CuAiNy0fGnoVhWbXBvJR1mkdsvCDUxvhX9cW60k2AhtH5s6w==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + nf3@0.3.19: + resolution: {integrity: sha512-tfOXX/ivQBL+4km/fzxQ0HWMmp1Ewx/YpFLbya080gXVfm26bZy0n4a5K5PPnqTLyuAHu0FobVpXXUadIiIwIQ==} + + nitro@3.0.260610-beta: + resolution: {integrity: sha512-KPb4L5yaF/Rx/xoGMpgHRJvZhbhGiqbRKOwwPLCH9jKTKTsEUHLjnJas85AeCzaswqa8Wi52eQBtRsODC4PS0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@vercel/queue': ^0.3.0 + dotenv: '*' + giget: '*' + jiti: ^2.7.0 + rollup: ^4.61.1 + vite: ^7 || ^8 + xml2js: ^0.6.2 + zephyr-agent: ^0.2.0 + peerDependenciesMeta: + '@vercel/queue': + optional: true + dotenv: + optional: true + giget: + optional: true + jiti: + optional: true + rollup: + optional: true + vite: + optional: true + xml2js: + optional: true + zephyr-agent: + optional: true + + ocache@0.1.5: + resolution: {integrity: sha512-kNNnkkVQup/QDvmTz8Q84wc2ntiyoVHDxa6eHWKt5qdGAmFRBIxy83rxgCYEjW0x06UJ9E3P6VgM2yY4rOBH4w==} + + ofetch@2.0.0-alpha.3: + resolution: {integrity: sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + rolldown@1.1.4: + resolution: {integrity: sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rou3@0.8.1: + resolution: {integrity: sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==} + + srvx@0.11.21: + resolution: {integrity: sha512-GWTHjKMeekX8CwJf4VU9Oo6mJpSGaflGMddbCvR+Cmmh9sslRMiGbAoqqZacE0r1ncARh6buCEETr2W52F8b1w==} + engines: {node: '>=20.16.0'} + hasBin: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + unstorage@2.0.0-alpha.7: + resolution: {integrity: sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog==} + peerDependencies: + '@azure/app-configuration': ^1.11.0 + '@azure/cosmos': ^4.9.1 + '@azure/data-tables': ^13.3.2 + '@azure/identity': ^4.13.0 + '@azure/keyvault-secrets': ^4.10.0 + '@azure/storage-blob': ^12.31.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.13.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.36.2 + '@vercel/blob': '>=0.27.3' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + chokidar: ^4 || ^5 + db0: '>=0.3.4' + idb-keyval: ^6.2.2 + ioredis: ^5.9.3 + lru-cache: ^11.2.6 + mongodb: ^6 || ^7 + ofetch: '*' + uploadthing: ^7.7.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + chokidar: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + lru-cache: + optional: true + mongodb: + optional: true + ofetch: + optional: true + uploadthing: + optional: true + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + +snapshots: + + '@ai-sdk/gateway@4.0.12(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 4.0.2 + '@ai-sdk/provider-utils': 5.0.5(zod@4.3.6) + '@vercel/oidc': 3.2.0 + zod: 4.3.6 + + '@ai-sdk/openai@4.0.0-beta.44(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 4.0.0-beta.14 + '@ai-sdk/provider-utils': 5.0.0-beta.30(zod@4.3.6) + zod: 4.3.6 + + '@ai-sdk/provider-utils@5.0.0-beta.30(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 4.0.0-beta.14 + '@standard-schema/spec': 1.1.0 + '@workflow/serde': 4.1.0 + eventsource-parser: 3.1.0 + zod: 4.3.6 + + '@ai-sdk/provider-utils@5.0.5(zod@4.3.6)': + dependencies: + '@ai-sdk/provider': 4.0.2 + '@standard-schema/spec': 1.1.0 + '@workflow/serde': 4.1.0 + eventsource-parser: 3.1.0 + zod: 4.3.6 + + '@ai-sdk/provider@4.0.0-beta.14': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/provider@4.0.2': + dependencies: + json-schema: 0.4.0 + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@oxc-project/types@0.138.0': {} + + '@rolldown/binding-android-arm64@1.1.4': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.4': + optional: true + + '@rolldown/binding-darwin-x64@1.1.4': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.4': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.4': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.4': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.4': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.4': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.4': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.4': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.4': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@standard-schema/spec@1.1.0': {} + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@vercel/oidc@3.2.0': {} + + '@workflow/serde@4.1.0': {} + + ai@7.0.15(zod@4.3.6): + dependencies: + '@ai-sdk/gateway': 4.0.12(zod@4.3.6) + '@ai-sdk/provider': 4.0.2 + '@ai-sdk/provider-utils': 5.0.5(zod@4.3.6) + zod: 4.3.6 + + consola@3.4.2: {} + + crossws@0.4.9(srvx@0.11.21): + optionalDependencies: + srvx: 0.11.21 + + db0@0.3.4: {} + + env-runner@0.1.16: + dependencies: + crossws: 0.4.9(srvx@0.11.21) + exsolve: 1.1.0 + httpxy: 0.5.4 + srvx: 0.11.21 + + eve@0.20.0(ai@7.0.15(zod@4.3.6)): + dependencies: + ai: 7.0.15(zod@4.3.6) + nitro: 3.0.260610-beta + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@netlify/runtime' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@vercel/queue' + - aws4fetch + - better-sqlite3 + - chokidar + - dotenv + - drizzle-orm + - giget + - idb-keyval + - ioredis + - jiti + - lru-cache + - miniflare + - mongodb + - mysql2 + - rollup + - sqlite3 + - uploadthing + - vite + - wrangler + - xml2js + - zephyr-agent + + eventsource-parser@3.1.0: {} + + exsolve@1.1.0: {} + + h3@2.0.1-rc.22(crossws@0.4.9(srvx@0.11.21)): + dependencies: + rou3: 0.8.1 + srvx: 0.11.21 + optionalDependencies: + crossws: 0.4.9(srvx@0.11.21) + + hookable@6.1.1: {} + + httpxy@0.5.4: {} + + json-schema@0.4.0: {} + + nf3@0.3.19: {} + + nitro@3.0.260610-beta: + dependencies: + consola: 3.4.2 + crossws: 0.4.9(srvx@0.11.21) + db0: 0.3.4 + env-runner: 0.1.16 + h3: 2.0.1-rc.22(crossws@0.4.9(srvx@0.11.21)) + hookable: 6.1.1 + nf3: 0.3.19 + ocache: 0.1.5 + ofetch: 2.0.0-alpha.3 + ohash: 2.0.11 + rolldown: 1.1.4 + srvx: 0.11.21 + unenv: 2.0.0-rc.24 + unstorage: 2.0.0-alpha.7(db0@0.3.4)(ofetch@2.0.0-alpha.3) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@netlify/runtime' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - better-sqlite3 + - chokidar + - drizzle-orm + - idb-keyval + - ioredis + - lru-cache + - miniflare + - mongodb + - mysql2 + - sqlite3 + - uploadthing + - wrangler + + ocache@0.1.5: + dependencies: + ohash: 2.0.11 + + ofetch@2.0.0-alpha.3: {} + + ohash@2.0.11: {} + + pathe@2.0.3: {} + + rolldown@1.1.4: + dependencies: + '@oxc-project/types': 0.138.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.4 + '@rolldown/binding-darwin-arm64': 1.1.4 + '@rolldown/binding-darwin-x64': 1.1.4 + '@rolldown/binding-freebsd-x64': 1.1.4 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.4 + '@rolldown/binding-linux-arm64-gnu': 1.1.4 + '@rolldown/binding-linux-arm64-musl': 1.1.4 + '@rolldown/binding-linux-ppc64-gnu': 1.1.4 + '@rolldown/binding-linux-s390x-gnu': 1.1.4 + '@rolldown/binding-linux-x64-gnu': 1.1.4 + '@rolldown/binding-linux-x64-musl': 1.1.4 + '@rolldown/binding-openharmony-arm64': 1.1.4 + '@rolldown/binding-wasm32-wasi': 1.1.4 + '@rolldown/binding-win32-arm64-msvc': 1.1.4 + '@rolldown/binding-win32-x64-msvc': 1.1.4 + + rou3@0.8.1: {} + + srvx@0.11.21: {} + + tslib@2.8.1: + optional: true + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + unstorage@2.0.0-alpha.7(db0@0.3.4)(ofetch@2.0.0-alpha.3): + optionalDependencies: + db0: 0.3.4 + ofetch: 2.0.0-alpha.3 + + zod@4.3.6: {} diff --git a/e2e/scenarios/eve-instrumentation/scenario.test.ts b/e2e/scenarios/eve-instrumentation/scenario.test.ts new file mode 100644 index 000000000..677ed4838 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/scenario.test.ts @@ -0,0 +1,116 @@ +import { beforeAll, describe, expect, test } from "vitest"; +import { resolveFileSnapshotPath } from "../../helpers/file-snapshot"; +import type { CapturedLogEvent } from "../../helpers/mock-braintrust-server"; +import { + prepareScenarioDir, + resolveScenarioDir, + withScenarioHarness, +} from "../../helpers/scenario-harness"; +import { matchSpanTreeSnapshot } from "../../helpers/span-tree"; +import { + findChildSpans, + findLatestChildSpan, + findLatestSpan, +} from "../../helpers/trace-selectors"; + +const originalScenarioDir = resolveScenarioDir(import.meta.url); +const scenarioDir = await prepareScenarioDir({ + scenarioDir: originalScenarioDir, +}); +const spanTreeSnapshotPath = resolveFileSnapshotPath( + import.meta.url, + "eve-instrumentation.span-tree.json", +); +const TIMEOUT_MS = 120_000; + +describe("eve instrumentation", () => { + let events: CapturedLogEvent[] = []; + + beforeAll(async () => { + await withScenarioHarness( + async ({ events: harnessEvents, runScenarioDir }) => { + await runScenarioDir({ + entry: "scenario.ts", + env: { + NODE_ENV: "development", + }, + runContext: { + originalScenarioDir, + variantKey: "eve-v0-20-0", + }, + scenarioDir, + timeoutMs: TIMEOUT_MS, + }); + events = harnessEvents(); + }, + ); + }, TIMEOUT_MS); + + test("captures a flat Eve turn trace", async () => { + const root = findLatestSpan(events, "eve.turn"); + const steps = findChildSpans(events, "eve.step", root?.span.id).sort( + (left, right) => + Number(left.metadata?.["eve.step.index"]) - + Number(right.metadata?.["eve.step.index"]), + ); + const search = findLatestChildSpan(events, "search", root?.span.id); + const read = findLatestChildSpan(events, "read", root?.span.id); + + expect(root).toBeDefined(); + expect(root?.span.type).toBe("task"); + expect(root?.metadata).toMatchObject({ + "eve.agent.name": "eve-instrumentation-scenario", + "eve.channel.kind": "http", + "eve.model.id": expect.any(String), + model: "gpt-5.4-mini", + provider: "openai", + }); + expect(root?.metrics?.completion_tokens).toEqual(expect.any(Number)); + expect(root?.metrics?.prompt_tokens).toEqual(expect.any(Number)); + expect(root?.metrics?.tokens).toEqual(expect.any(Number)); + expect(root?.output).toContain("Final answer from read"); + + expect(steps).toHaveLength(3); + expect(steps.map((step) => step.span.type)).toEqual(["llm", "llm", "llm"]); + for (const step of steps) { + expect(step.span.parentIds).toEqual([root?.span.id]); + expect(step.input).toEqual(expect.arrayContaining([expect.anything()])); + expect(step.metadata).toMatchObject({ + "eve.model.id": expect.any(String), + model: "gpt-5.4-mini", + provider: "openai", + }); + } + + expect(search).toBeDefined(); + expect(search?.span.type).toBe("tool"); + expect(search?.span.parentIds).toEqual([root?.span.id]); + expect(search?.input).toMatchObject({ + query: expect.stringContaining("Braintrust Eve instrumentation"), + }); + expect(search?.output).toMatchObject({ + title: "Eve instrumentation", + }); + + expect(read).toBeDefined(); + expect(read?.span.type).toBe("tool"); + expect(read?.span.parentIds).toEqual([root?.span.id]); + expect(read?.input).toMatchObject({ + url: "https://eve.dev/docs/guides/instrumentation", + }); + expect(read?.output).toMatchObject({ + section: "Runtime context", + title: "Eve instrumentation", + }); + + await matchSpanTreeSnapshot(events, spanTreeSnapshotPath, { + normalize: { + additionalProviderIdKeys: [ + "eve.session.id", + "eve.tool.call_id", + "eve.turn.id", + ], + }, + }); + }); +}); diff --git a/e2e/scenarios/eve-instrumentation/scenario.ts b/e2e/scenarios/eve-instrumentation/scenario.ts new file mode 100644 index 000000000..4d1027b1e --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/scenario.ts @@ -0,0 +1,208 @@ +import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; +import { once } from "node:events"; +import net from "node:net"; +import path from "node:path"; +import { runMain } from "../../helpers/scenario-runtime"; + +async function main() { + const eveBin = path.join( + process.cwd(), + "node_modules", + ".bin", + process.platform === "win32" ? "eve.cmd" : "eve", + ); + + await runProcess(eveBin, ["build"], 90_000); + + const port = await getFreePort(); + const server = spawn( + eveBin, + ["start", "--host", "127.0.0.1", "--port", String(port)], + { + cwd: process.cwd(), + env: process.env, + stdio: ["ignore", "pipe", "pipe"], + }, + ); + const output = captureOutput(server); + + try { + const baseUrl = `http://127.0.0.1:${port}`; + await waitForEve(baseUrl, server, output); + + const response = await fetch(`${baseUrl}/eve/v1/session`, { + body: JSON.stringify({ + message: "Run the Braintrust Eve instrumentation e2e scenario", + }), + headers: { "content-type": "application/json" }, + method: "POST", + }); + if (!response.ok) { + throw new Error( + `Eve session create failed with ${response.status}: ${await response.text()}`, + ); + } + + const body = (await response.json()) as { sessionId?: string }; + if (!body.sessionId) { + throw new Error(`Eve session create did not return a sessionId`); + } + + await streamUntilTurnCompleted(baseUrl, body.sessionId); + await new Promise((resolve) => setTimeout(resolve, 5000)); + } finally { + await stopServer(server); + } +} + +async function getFreePort(): Promise { + const server = net.createServer(); + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", () => resolve()); + }); + const address = server.address(); + await new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }); + if (!address || typeof address === "string") { + throw new Error("Could not allocate a port for eve"); + } + return address.port; +} + +async function runProcess( + command: string, + args: string[], + timeoutMs: number, +): Promise { + const child = spawn(command, args, { + cwd: process.cwd(), + env: process.env, + stdio: ["ignore", "pipe", "pipe"], + }); + const output = captureOutput(child); + const timeout = setTimeout(() => child.kill("SIGTERM"), timeoutMs); + try { + const [code] = (await once(child, "close")) as [number | null]; + if (code !== 0) { + throw new Error( + `${path.basename(command)} ${args.join(" ")} failed with code ${code}\n${output()}`, + ); + } + } finally { + clearTimeout(timeout); + } +} + +function captureOutput(child: ChildProcessWithoutNullStreams): () => string { + let stdout = ""; + let stderr = ""; + child.stdout.on("data", (chunk) => { + stdout += String(chunk); + }); + child.stderr.on("data", (chunk) => { + stderr += String(chunk); + }); + return () => `STDOUT:\n${stdout}\nSTDERR:\n${stderr}`; +} + +async function waitForEve( + baseUrl: string, + server: ChildProcessWithoutNullStreams, + output: () => string, +): Promise { + const startedAt = Date.now(); + while (Date.now() - startedAt < 45_000) { + if (server.exitCode !== null) { + throw new Error( + `eve start exited early with code ${server.exitCode}\n${output()}`, + ); + } + try { + const response = await fetch(`${baseUrl}/eve/v1/health`); + if (response.ok) { + return; + } + } catch { + // Keep polling until the server starts accepting connections. + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for eve start\n${output()}`); +} + +async function streamUntilTurnCompleted( + baseUrl: string, + sessionId: string, +): Promise { + const controller = new AbortController(); + const response = await fetch( + `${baseUrl}/eve/v1/session/${sessionId}/stream`, + { + signal: controller.signal, + }, + ); + if (!response.ok || !response.body) { + throw new Error( + `Eve stream failed with ${response.status}: ${await response.text()}`, + ); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + try { + while (true) { + const { done, value } = await reader.read(); + if (done) { + throw new Error("Eve stream ended before turn.completed"); + } + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split("\n"); + buffer = lines.pop() ?? ""; + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed) { + continue; + } + const event = JSON.parse(trimmed) as { + data?: { message?: string }; + type?: string; + }; + if ( + event.type === "step.failed" || + event.type === "turn.failed" || + event.type === "session.failed" + ) { + throw new Error( + `Eve emitted ${event.type}: ${event.data?.message ?? trimmed}`, + ); + } + if (event.type === "turn.completed") { + return; + } + } + } + } finally { + controller.abort(); + await reader.cancel().catch(() => undefined); + } +} + +async function stopServer( + server: ChildProcessWithoutNullStreams, +): Promise { + if (server.exitCode !== null) { + return; + } + server.kill("SIGTERM"); + const timeout = setTimeout(() => server.kill("SIGKILL"), 5_000); + try { + await once(server, "close"); + } finally { + clearTimeout(timeout); + } +} + +runMain(main); diff --git a/e2e/scenarios/eve-instrumentation/tsconfig.json b/e2e/scenarios/eve-instrumentation/tsconfig.json new file mode 100644 index 000000000..e334ac972 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "target": "ES2022" + } +} diff --git a/js/src/exports.ts b/js/src/exports.ts index 6ed3bf0e0..a2b0a38f6 100644 --- a/js/src/exports.ts +++ b/js/src/exports.ts @@ -175,6 +175,7 @@ export { BraintrustMiddleware, wrapAISDKModel, } from "./wrappers/ai-sdk"; +export { braintrustEveHook } from "./instrumentation/plugins/eve-plugin"; export { wrapAnthropic } from "./wrappers/anthropic"; export { BraintrustObservabilityExporter, diff --git a/js/src/instrumentation/index.ts b/js/src/instrumentation/index.ts index 385101d77..0c0d4f9db 100644 --- a/js/src/instrumentation/index.ts +++ b/js/src/instrumentation/index.ts @@ -23,6 +23,7 @@ export { braintrustFlueInstrumentation, braintrustFlueObserver, } from "./plugins/flue-plugin"; +export { braintrustEveHook } from "./plugins/eve-plugin"; // Re-export core types for external instrumentation packages export type { diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts new file mode 100644 index 000000000..f10951c19 --- /dev/null +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -0,0 +1,344 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { configureNode } from "../../node/config"; +import { _exportsForTestingOnly, initLogger } from "../../logger"; +import { braintrustEveHook } from "./eve-plugin"; +import type { + EveHandleMessageStreamEvent, + EveHookContext, +} from "../../vendor-sdk-types/eve"; + +try { + configureNode(); +} catch { + // Best-effort initialization for test environments. +} + +describe("braintrustEveHook", () => { + let backgroundLogger: ReturnType< + typeof _exportsForTestingOnly.useTestBackgroundLogger + >; + + beforeAll(async () => { + await _exportsForTestingOnly.simulateLoginForTests(); + }); + + beforeEach(() => { + Reflect.deleteProperty(globalThis, Symbol.for("braintrust.eve.bridge")); + backgroundLogger = _exportsForTestingOnly.useTestBackgroundLogger(); + initLogger({ + projectName: "eve-plugin.test.ts", + projectId: "test-project-id", + }); + }); + + afterEach(() => { + Reflect.deleteProperty(globalThis, Symbol.for("braintrust.eve.bridge")); + _exportsForTestingOnly.clearTestBackgroundLogger(); + }); + + it("returns an Eve hook definition", () => { + const hook = braintrustEveHook(); + + expect(Object.keys(hook)).toEqual(["events"]); + expect(typeof hook.events?.["*"]).toBe("function"); + }); + + it("records a flat Eve turn with session model metadata", async () => { + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { + agent: { name: "eve-test-agent" }, + channel: { kind: "http" }, + session: { id: "session-flat-tree" }, + }; + const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); + const expectedModelMetadata = { + "eve.model.id": "eve-mock/braintrust-eve-mock", + model: "braintrust-eve-mock", + provider: "eve-mock", + }; + + emit({ + data: { + runtime: { + agentId: "agent-id", + agentName: "eve-test-agent", + eveVersion: "0.20.0", + modelId: "eve-mock/braintrust-eve-mock", + }, + }, + meta: { at: "2026-01-01T00:00:00.000Z" }, + type: "session.started", + }); + emit({ + data: { sequence: 0, turnId: "turn-flat-tree" }, + meta: { at: "2026-01-01T00:00:00.010Z" }, + type: "turn.started", + }); + emit({ + data: { + message: "Search then read", + sequence: 0, + turnId: "turn-flat-tree", + }, + meta: { at: "2026-01-01T00:00:00.020Z" }, + type: "message.received", + }); + emit({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-flat-tree" }, + meta: { at: "2026-01-01T00:00:00.030Z" }, + type: "step.started", + }); + emit({ + data: { + actions: [ + { + callId: "call-search", + input: { query: "Eve instrumentation" }, + kind: "tool-call", + toolName: "search", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-flat-tree", + }, + meta: { at: "2026-01-01T00:00:00.040Z" }, + type: "actions.requested", + }); + emit({ + data: { + error: undefined, + result: { + callId: "call-search", + kind: "tool-result", + output: { hits: ["eve.dev/docs"] }, + toolName: "search", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-flat-tree", + }, + meta: { at: "2026-01-01T00:00:00.050Z" }, + type: "action.result", + }); + emit({ + data: { + finishReason: "tool-calls", + sequence: 0, + stepIndex: 0, + turnId: "turn-flat-tree", + usage: { + cacheReadTokens: 3, + cacheWriteTokens: 2, + costUsd: 0.001, + inputTokens: 10, + outputTokens: 5, + }, + }, + meta: { at: "2026-01-01T00:00:00.060Z" }, + type: "step.completed", + }); + emit({ + data: { sequence: 0, stepIndex: 1, turnId: "turn-flat-tree" }, + meta: { at: "2026-01-01T00:00:00.070Z" }, + type: "step.started", + }); + emit({ + data: { + finishReason: "stop", + message: "Here is the Eve instrumentation guide.", + sequence: 0, + stepIndex: 1, + turnId: "turn-flat-tree", + }, + meta: { at: "2026-01-01T00:00:00.080Z" }, + type: "message.completed", + }); + emit({ + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 1, + turnId: "turn-flat-tree", + usage: { + inputTokens: 20, + outputTokens: 8, + }, + }, + meta: { at: "2026-01-01T00:00:00.090Z" }, + type: "step.completed", + }); + emit({ + data: { sequence: 0, turnId: "turn-flat-tree" }, + meta: { at: "2026-01-01T00:00:00.100Z" }, + type: "turn.completed", + }); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const root = spans.find( + (span) => span.span_attributes?.name === "eve.turn", + ); + const steps = spans + .filter((span) => + String(span.span_attributes?.name).startsWith("eve.step"), + ) + .sort( + (left, right) => + Number(left.metadata?.["eve.step.index"]) - + Number(right.metadata?.["eve.step.index"]), + ); + const tool = spans.find((span) => span.span_attributes?.name === "search"); + + expect(spans.map((span) => span.span_attributes?.name)).toEqual([ + "eve.turn", + "eve.step", + "search", + "eve.step", + ]); + expect(root).toMatchObject({ + input: [{ content: "Search then read", role: "user" }], + metadata: { + ...expectedModelMetadata, + "eve.agent.name": "eve-test-agent", + "eve.channel.kind": "http", + "eve.session.id": "session-flat-tree", + "eve.turn.id": "turn-flat-tree", + }, + metrics: { + completion_tokens: 13, + estimated_cost: 0.001, + prompt_cached_tokens: 3, + prompt_cache_creation_tokens: 2, + prompt_tokens: 30, + tokens: 43, + }, + output: "Here is the Eve instrumentation guide.", + span_attributes: { + name: "eve.turn", + type: "task", + }, + }); + expect(steps).toHaveLength(2); + expect(steps.map((span) => span.span_attributes?.name)).toEqual([ + "eve.step", + "eve.step", + ]); + expect(steps.map((span) => span.span_attributes?.type)).toEqual([ + "llm", + "llm", + ]); + expect(steps.map((span) => span.span_parents)).toEqual([ + [root?.span_id], + [root?.span_id], + ]); + for (const [index, step] of steps.entries()) { + expect(step.metadata).toMatchObject({ + ...expectedModelMetadata, + "eve.step.index": index, + }); + } + expect(steps[0]?.input).toEqual([ + { content: "Search then read", role: "user" }, + ]); + expect(steps[1]?.input).toMatchObject([ + { content: "Search then read", role: "user" }, + { + content: null, + role: "assistant", + tool_calls: [ + { + function: { + arguments: JSON.stringify({ query: "Eve instrumentation" }), + name: "search", + }, + id: "call-search", + type: "function", + }, + ], + }, + { + content: { hits: ["eve.dev/docs"] }, + name: "search", + role: "tool", + tool_call_id: "call-search", + }, + ]); + expect(tool).toMatchObject({ + input: { query: "Eve instrumentation" }, + metadata: { + "eve.step.index": 0, + "eve.tool.call_id": "call-search", + "eve.tool.name": "search", + }, + output: { hits: ["eve.dev/docs"] }, + span_attributes: { + name: "search", + type: "tool", + }, + span_parents: [root?.span_id], + }); + expect(tool?.metadata).not.toHaveProperty("eve.model.id"); + expect(tool?.metadata).not.toHaveProperty("model"); + expect(tool?.metadata).not.toHaveProperty("provider"); + expect(steps[0]?.output).toMatchObject({ + message: { + tool_calls: [ + { + function: { + arguments: JSON.stringify({ query: "Eve instrumentation" }), + name: "search", + }, + id: "call-search", + type: "function", + }, + ], + }, + }); + expect(steps[1]?.output).toMatchObject({ + finish_reason: "stop", + message: { + content: "Here is the Eve instrumentation guide.", + role: "assistant", + }, + }); + }); + + it("does not throw when Eve emits malformed events or failures", async () => { + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + expect(() => wildcard?.({ bad: true } as never, {})).not.toThrow(); + expect(() => + wildcard?.( + { + data: { + code: "boom", + message: "step failed", + sequence: 0, + stepIndex: 0, + turnId: "turn-missing-session", + }, + type: "step.failed", + }, + {}, + ), + ).not.toThrow(); + expect(() => + wildcard?.( + { + data: { runtime: { modelId: 123 } }, + type: "session.started", + } as never, + { session: { id: "session-malformed-runtime" } }, + ), + ).not.toThrow(); + + const spans = await backgroundLogger.drain(); + expect(spans).toEqual([]); + }); +}); diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts new file mode 100644 index 000000000..4883631ca --- /dev/null +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -0,0 +1,953 @@ +import { toLoggedError } from "../core"; +import { debugLogger } from "../../debug-logger"; +import { flush, logError, startSpan, withCurrent } from "../../logger"; +import type { Span, StartSpanArgs } from "../../logger"; +import { SpanTypeAttribute, isObject } from "../../../util/index"; +import type { + EveHandleMessageStreamEvent, + EveHookContext, + EveHookDefinition, + EveRuntimeToolCallActionRequest, + EveRuntimeToolResultActionResult, +} from "../../vendor-sdk-types/eve"; + +type SpanState = { + metadata: Record; + span: Span; +}; + +type StepState = SpanState & { + metrics: Record; + output?: unknown; + toolCalls: Array<{ + function: { + arguments: string; + name: string; + }; + id: string; + type: "function"; + }>; +}; + +type TurnState = SpanState & { + inputLogged: boolean; + messages: unknown[]; + metrics: Record; + output?: unknown; + stepsByIndex: Map; +}; + +type ToolState = SpanState & { + stepIndex?: number; + turnKey: string; +}; + +const EVE_BRIDGE = Symbol.for("braintrust.eve.bridge"); + +/** Manual hook instrumentation for eve runtime stream events. */ +export function braintrustEveHook(): EveHookDefinition { + return { + events: { + "*": (event: EveHandleMessageStreamEvent, ctx: EveHookContext) => { + getEveBridge().handle(event, ctx); + }, + }, + }; +} + +function getEveBridge(): EveBridge { + const existing = Reflect.get(globalThis, EVE_BRIDGE); + if (existing instanceof EveBridge) { + return existing; + } + const bridge = new EveBridge(); + Reflect.set(globalThis, EVE_BRIDGE, bridge); + return bridge; +} + +class EveBridge { + private sessionsById = new Map< + string, + { metadata: Record } + >(); + private toolsByCallKey = new Map(); + private turnsByKey = new Map(); + + handle(event: unknown, ctx: unknown): void { + if (!isObject(event)) { + return; + } + + try { + this.handleEvent(event as EveHandleMessageStreamEvent, ctx); + } catch (error) { + logInstrumentationError("Eve hook event", error); + } + } + + private handleEvent(event: EveHandleMessageStreamEvent, ctx: unknown): void { + switch (event.type) { + case "session.started": + this.handleSessionStarted(event, ctx); + return; + case "turn.started": + this.handleTurnStarted(event, ctx); + return; + case "message.received": + this.handleMessageReceived(event, ctx); + return; + case "step.started": + this.handleStepStarted(event, ctx); + return; + case "message.completed": + this.handleMessageCompleted(event, ctx); + return; + case "result.completed": + this.handleResultCompleted(event, ctx); + return; + case "actions.requested": + this.handleActionsRequested(event, ctx); + return; + case "action.result": + this.handleActionResult(event, ctx); + return; + case "step.completed": + this.handleStepCompleted(event, ctx); + return; + case "step.failed": + this.handleStepFailed(event, ctx); + return; + case "turn.completed": + this.handleTurnCompleted(event, ctx); + return; + case "turn.failed": + this.handleTurnFailed(event, ctx); + return; + case "session.failed": + this.handleSessionFailed(event, ctx); + return; + case "session.completed": + this.handleSessionCompleted(ctx); + return; + default: + return; + } + } + + private handleSessionStarted( + event: Extract, + ctx: unknown, + ): void { + const sessionId = sessionIdFromContext(ctx); + const metadata = modelMetadataFromRuntime(event.data.runtime); + if (!sessionId || Object.keys(metadata).length === 0) { + return; + } + + this.sessionsById.set(sessionId, { metadata }); + for (const [key, turn] of this.turnsByKey) { + if (!key.startsWith(`${sessionId}:`)) { + continue; + } + + turn.metadata = { ...turn.metadata, ...metadata }; + safeLog(turn.span, { metadata: turn.metadata }); + for (const step of turn.stepsByIndex.values()) { + step.metadata = { ...step.metadata, ...metadata }; + safeLog(step.span, { metadata: step.metadata }); + } + } + } + + private handleTurnStarted( + event: Extract, + ctx: unknown, + ): void { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + + const key = turnKey(sessionId, event.data.turnId); + const metadata = this.turnMetadata(sessionId, event, ctx); + const existing = this.turnsByKey.get(key); + if (existing) { + existing.metadata = metadata; + safeLog(existing.span, { metadata }); + return; + } + + const span = startSpan({ + event: { metadata }, + name: "eve.turn", + spanAttributes: { type: SpanTypeAttribute.TASK }, + startTime: eventTime(event), + }); + safeLog(span, { metadata }); + this.turnsByKey.set(key, { + inputLogged: false, + messages: [], + metadata, + metrics: {}, + span, + stepsByIndex: new Map(), + }); + } + + private handleMessageReceived( + event: Extract, + ctx: unknown, + ): void { + const turn = this.ensureTurn(event, ctx); + if (!turn) { + return; + } + + const input = [{ content: event.data.message, role: "user" }]; + turn.messages.push(...input); + if (!turn.inputLogged) { + turn.inputLogged = true; + safeLog(turn.span, { input }); + } + } + + private handleStepStarted( + event: Extract, + ctx: unknown, + ): void { + const turn = this.ensureTurn(event, ctx); + if (!turn) { + return; + } + + const metadata = { + ...turn.metadata, + "eve.step.index": event.data.stepIndex, + }; + const span = startEveSpan(turn.span, { + event: { + input: turn.messages.length > 0 ? [...turn.messages] : undefined, + metadata, + }, + name: "eve.step", + spanAttributes: { type: SpanTypeAttribute.LLM }, + startTime: eventTime(event), + }); + safeLog(span, { + input: turn.messages.length > 0 ? [...turn.messages] : undefined, + metadata, + }); + + turn.stepsByIndex.set(event.data.stepIndex, { + metadata, + metrics: {}, + span, + toolCalls: [], + }); + } + + private handleMessageCompleted( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (!step) { + return; + } + + step.output = { + finish_reason: event.data.finishReason, + message: { + content: event.data.message, + role: "assistant", + ...(step.toolCalls.length > 0 + ? { tool_calls: [...step.toolCalls] } + : {}), + }, + }; + + const turn = this.turnForEvent(event, ctx); + if (turn && event.data.finishReason !== "tool-calls") { + turn.output = event.data.message; + turn.messages.push({ + content: event.data.message, + role: "assistant", + }); + } + } + + private handleResultCompleted( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (step) { + step.output = event.data.result; + } + + const turn = this.turnForEvent(event, ctx); + if (turn) { + turn.output = event.data.result; + } + } + + private handleActionsRequested( + event: Extract, + ctx: unknown, + ): void { + const turn = this.ensureTurn(event, ctx); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return; + } + + const step = turn.stepsByIndex.get(event.data.stepIndex); + const toolActions = event.data.actions.filter(isToolCallAction); + if (toolActions.length === 0) { + return; + } + + turn.messages.push({ + content: null, + role: "assistant", + tool_calls: toolActions.map(openAIToolCallFromAction), + }); + + for (const action of toolActions) { + step?.toolCalls.push(openAIToolCallFromAction(action)); + if (step) { + if (step.output && isObject(step.output)) { + const message = Reflect.get(step.output, "message"); + if (isObject(message)) { + Reflect.set(message, "tool_calls", [...step.toolCalls]); + } + } else { + step.output = { + message: { + content: null, + role: "assistant", + tool_calls: [...step.toolCalls], + }, + }; + } + } + + const key = toolKey(sessionId, action.callId); + if (this.toolsByCallKey.has(key)) { + continue; + } + + const metadata = { + ...withoutModelMetadata(turn.metadata), + "eve.step.index": event.data.stepIndex, + "eve.tool.call_id": action.callId, + "eve.tool.name": action.toolName, + }; + const span = startEveSpan(turn.span, { + event: { + input: action.input, + metadata, + }, + name: action.toolName, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + startTime: eventTime(event), + }); + safeLog(span, { + input: action.input, + metadata, + }); + this.toolsByCallKey.set(key, { + metadata, + span, + stepIndex: event.data.stepIndex, + turnKey: turnKey(sessionId, event.data.turnId), + }); + } + } + + private handleActionResult( + event: Extract, + ctx: unknown, + ): void { + if (!isToolResult(event.data.result)) { + return; + } + + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + + const key = toolKey(sessionId, event.data.result.callId); + const tool = + this.toolsByCallKey.get(key) ?? + this.startSyntheticTool(event, ctx, event.data.result); + if (!tool) { + return; + } + + const failed = + event.data.status === "failed" || + event.data.result.isError === true || + event.data.error !== undefined; + const metadata = { + ...tool.metadata, + ...(event.data.status ? { "eve.action.status": event.data.status } : {}), + }; + safeLog(tool.span, { + ...(failed + ? { + error: toLoggedError( + event.data.error?.message ?? event.data.result.output, + ), + } + : {}), + metadata, + output: event.data.result.output, + }); + + const turn = this.turnForEvent(event, ctx); + turn?.messages.push({ + content: event.data.result.output, + name: event.data.result.toolName, + role: "tool", + tool_call_id: event.data.result.callId, + }); + + safeEnd(tool.span, eventTime(event)); + this.toolsByCallKey.delete(key); + } + + private handleStepCompleted( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (!step) { + return; + } + + const metrics = metricsFromUsage(event.data.usage); + step.metrics = { ...step.metrics, ...metrics }; + const metadata = { + ...step.metadata, + "eve.finish_reason": event.data.finishReason, + ...(event.data.providerMetadata?.gateway?.generationId + ? { + "eve.gateway.generation_id": + event.data.providerMetadata.gateway.generationId, + } + : {}), + }; + + safeLog(step.span, { + metadata, + metrics, + output: step.output, + }); + safeEnd(step.span, eventTime(event)); + + const turn = this.turnForEvent(event, ctx); + if (turn) { + addMetrics(turn.metrics, metrics); + turn.stepsByIndex.delete(event.data.stepIndex); + } + } + + private handleStepFailed( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (step) { + logError( + step.span, + errorFromMessage( + event.data.message, + event.data.code, + event.data.details, + ), + ); + safeEnd(step.span, eventTime(event)); + } + + const turn = this.turnForEvent(event, ctx); + turn?.stepsByIndex.delete(event.data.stepIndex); + } + + private handleTurnCompleted( + event: Extract, + ctx: unknown, + ): void { + const turn = this.turnForEvent(event, ctx); + if (!turn) { + return; + } + + this.endOpenChildrenForTurn(turn, eventTime(event)); + safeLog(turn.span, { + metadata: turn.metadata, + metrics: turn.metrics, + output: turn.output, + }); + safeEnd(turn.span, eventTime(event)); + this.cleanupTurn(event, ctx); + + void flush().catch((error) => { + logInstrumentationError("Eve flush", error); + }); + } + + private handleTurnFailed( + event: Extract, + ctx: unknown, + ): void { + const turn = this.turnForEvent(event, ctx); + if (!turn) { + return; + } + + this.endOpenChildrenForTurn(turn, eventTime(event)); + logError( + turn.span, + errorFromMessage(event.data.message, event.data.code, event.data.details), + ); + safeEnd(turn.span, eventTime(event)); + this.cleanupTurn(event, ctx); + + void flush().catch((error) => { + logInstrumentationError("Eve flush", error); + }); + } + + private handleSessionFailed( + event: Extract, + ctx: unknown, + ): void { + const sessionId = event.data.sessionId || sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + + for (const [key, turn] of this.turnsByKey) { + if (!key.startsWith(`${sessionId}:`)) { + continue; + } + this.endOpenChildrenForTurn(turn, eventTime(event)); + logError( + turn.span, + errorFromMessage( + event.data.message, + event.data.code, + event.data.details, + ), + ); + safeEnd(turn.span, eventTime(event)); + this.turnsByKey.delete(key); + } + + for (const [key, tool] of this.toolsByCallKey) { + if (key.startsWith(`${sessionId}:`)) { + safeEnd(tool.span, eventTime(event)); + this.toolsByCallKey.delete(key); + } + } + + this.sessionsById.delete(sessionId); + + void flush().catch((error) => { + logInstrumentationError("Eve flush", error); + }); + } + + private handleSessionCompleted(ctx: unknown): void { + const sessionId = sessionIdFromContext(ctx); + if (sessionId) { + this.sessionsById.delete(sessionId); + } + } + + private ensureTurn( + event: Extract< + EveHandleMessageStreamEvent, + { + data: { readonly sequence: number; readonly turnId: string }; + } + >, + ctx: unknown, + ): TurnState | undefined { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return undefined; + } + + const key = turnKey(sessionId, event.data.turnId); + const existing = this.turnsByKey.get(key); + if (existing) { + return existing; + } + + const metadata = this.turnMetadata(sessionId, event, ctx); + const span = startSpan({ + event: { metadata }, + name: "eve.turn", + spanAttributes: { type: SpanTypeAttribute.TASK }, + startTime: eventTime(event), + }); + safeLog(span, { metadata }); + const state = { + inputLogged: false, + messages: [], + metadata, + metrics: {}, + span, + stepsByIndex: new Map(), + }; + this.turnsByKey.set(key, state); + return state; + } + + private turnForEvent( + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly turnId: string } } + >, + ctx: unknown, + ): TurnState | undefined { + const sessionId = sessionIdFromContext(ctx); + return sessionId + ? this.turnsByKey.get(turnKey(sessionId, event.data.turnId)) + : undefined; + } + + private stepForEvent( + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly stepIndex: number; readonly turnId: string } } + >, + ctx: unknown, + ): StepState | undefined { + return this.turnForEvent(event, ctx)?.stepsByIndex.get( + event.data.stepIndex, + ); + } + + private startSyntheticTool( + event: Extract, + ctx: unknown, + result: EveRuntimeToolResultActionResult, + ): ToolState | undefined { + const turn = this.ensureTurn(event, ctx); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return undefined; + } + + const metadata = { + ...withoutModelMetadata(turn.metadata), + "eve.step.index": event.data.stepIndex, + "eve.tool.call_id": result.callId, + "eve.tool.name": result.toolName, + }; + const span = startEveSpan(turn.span, { + event: { metadata }, + name: result.toolName, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + startTime: eventTime(event), + }); + safeLog(span, { metadata }); + const state = { + metadata, + span, + stepIndex: event.data.stepIndex, + turnKey: turnKey(sessionId, event.data.turnId), + }; + this.toolsByCallKey.set(toolKey(sessionId, result.callId), state); + return state; + } + + private endOpenChildrenForTurn(turn: TurnState, endTime: number | undefined) { + for (const step of turn.stepsByIndex.values()) { + safeEnd(step.span, endTime); + } + turn.stepsByIndex.clear(); + + for (const [key, tool] of this.toolsByCallKey) { + if (tool.turnKey !== this.keyForTurnState(turn)) { + continue; + } + safeEnd(tool.span, endTime); + this.toolsByCallKey.delete(key); + } + } + + private cleanupTurn( + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly turnId: string } } + >, + ctx: unknown, + ): void { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + const key = turnKey(sessionId, event.data.turnId); + this.turnsByKey.delete(key); + } + + private keyForTurnState(turn: TurnState): string | undefined { + const sessionId = turn.metadata["eve.session.id"]; + const turnId = turn.metadata["eve.turn.id"]; + return typeof sessionId === "string" && typeof turnId === "string" + ? turnKey(sessionId, turnId) + : undefined; + } + + private turnMetadata( + sessionId: string, + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly sequence: number; readonly turnId: string } } + >, + ctx: unknown, + ): Record { + return { + ...baseMetadata({ + channelKind: channelKindFromContext(ctx), + sessionId, + turnId: event.data.turnId, + turnSequence: event.data.sequence, + }), + ...agentMetadataFromContext(ctx), + ...(this.sessionsById.get(sessionId)?.metadata ?? {}), + }; + } +} + +function baseMetadata(input: { + channelKind?: string; + sessionId: string; + turnId: string; + turnSequence: number; +}): Record { + return { + "eve.channel.kind": input.channelKind ?? "unknown", + "eve.session.id": input.sessionId, + "eve.turn.id": input.turnId, + "eve.turn.sequence": input.turnSequence, + }; +} + +function agentMetadataFromContext(ctx: unknown): Record { + if (!isObject(ctx)) { + return {}; + } + const agent = Reflect.get(ctx, "agent"); + if (!isObject(agent)) { + return {}; + } + const name = Reflect.get(agent, "name"); + const nodeId = Reflect.get(agent, "nodeId"); + return { + ...(typeof name === "string" ? { "eve.agent.name": name } : {}), + ...(typeof nodeId === "string" ? { "eve.agent.node_id": nodeId } : {}), + }; +} + +function modelMetadataFromRuntime(runtime: unknown): Record { + if (!isObject(runtime)) { + return {}; + } + const modelId = Reflect.get(runtime, "modelId"); + return typeof modelId === "string" ? modelMetadataFromModelId(modelId) : {}; +} + +function modelMetadataFromModelId(modelId: string): Record { + const normalized = modelId.trim(); + if (!normalized) { + return {}; + } + + const slashIndex = normalized.indexOf("/"); + if (slashIndex > 0 && slashIndex < normalized.length - 1) { + return { + "eve.model.id": normalized, + model: normalized.slice(slashIndex + 1), + provider: normalized.slice(0, slashIndex), + }; + } + + return { + "eve.model.id": normalized, + model: normalized, + }; +} + +function withoutModelMetadata( + metadata: Record, +): Record { + const { + ["eve.model.id"]: _modelId, + model: _model, + provider: _provider, + ...rest + } = metadata; + return rest; +} + +function sessionIdFromContext(ctx: unknown): string | undefined { + if (!isObject(ctx)) { + return undefined; + } + const session = Reflect.get(ctx, "session"); + if (!isObject(session)) { + return undefined; + } + const id = Reflect.get(session, "id"); + return typeof id === "string" ? id : undefined; +} + +function channelKindFromContext(ctx: unknown): string | undefined { + if (!isObject(ctx)) { + return undefined; + } + const channel = Reflect.get(ctx, "channel"); + if (!isObject(channel)) { + return undefined; + } + const kind = Reflect.get(channel, "kind"); + return typeof kind === "string" ? kind : undefined; +} + +function isToolCallAction( + action: unknown, +): action is EveRuntimeToolCallActionRequest { + return ( + isObject(action) && + Reflect.get(action, "kind") === "tool-call" && + typeof Reflect.get(action, "callId") === "string" && + typeof Reflect.get(action, "toolName") === "string" && + isObject(Reflect.get(action, "input")) + ); +} + +function isToolResult( + result: unknown, +): result is EveRuntimeToolResultActionResult { + return ( + isObject(result) && + Reflect.get(result, "kind") === "tool-result" && + typeof Reflect.get(result, "callId") === "string" && + typeof Reflect.get(result, "toolName") === "string" + ); +} + +function openAIToolCallFromAction(action: EveRuntimeToolCallActionRequest) { + return { + function: { + arguments: JSON.stringify(action.input), + name: action.toolName, + }, + id: action.callId, + type: "function" as const, + }; +} + +function metricsFromUsage( + usage: Extract< + EveHandleMessageStreamEvent, + { type: "step.completed" } + >["data"]["usage"], +): Record { + if (!usage) { + return {}; + } + + const total = + typeof usage.inputTokens === "number" && + typeof usage.outputTokens === "number" + ? usage.inputTokens + usage.outputTokens + : undefined; + return { + ...(typeof usage.inputTokens === "number" + ? { prompt_tokens: usage.inputTokens } + : {}), + ...(typeof usage.outputTokens === "number" + ? { completion_tokens: usage.outputTokens } + : {}), + ...(typeof total === "number" ? { tokens: total } : {}), + ...(typeof usage.cacheReadTokens === "number" + ? { prompt_cached_tokens: usage.cacheReadTokens } + : {}), + ...(typeof usage.cacheWriteTokens === "number" + ? { prompt_cache_creation_tokens: usage.cacheWriteTokens } + : {}), + ...(typeof usage.costUsd === "number" + ? { estimated_cost: usage.costUsd } + : {}), + }; +} + +function addMetrics( + target: Record, + metrics: Record, +): void { + for (const [key, value] of Object.entries(metrics)) { + target[key] = (target[key] ?? 0) + value; + } +} + +function errorFromMessage( + message: string, + code: string, + details?: unknown, +): Error { + const error = new Error(`${code}: ${message}`); + if (details !== undefined) { + error.cause = details; + } + return error; +} + +function eventTime(event: { + readonly meta?: { readonly at: string }; +}): number | undefined { + if (!event.meta?.at) { + return undefined; + } + const timestamp = Date.parse(event.meta.at); + return Number.isFinite(timestamp) ? timestamp / 1000 : undefined; +} + +function turnKey(sessionId: string, turnId: string): string { + return `${sessionId}:${turnId}`; +} + +function toolKey(sessionId: string, callId: string): string { + return `${sessionId}:${callId}`; +} + +function startEveSpan(parent: Span, args: StartSpanArgs): Span { + return withCurrent(parent, () => startSpan(args)); +} + +function safeLog(span: Span, event: Parameters[0]): void { + try { + span.log(event); + } catch (error) { + logInstrumentationError("Eve span log", error); + } +} + +function safeEnd(span: Span, endTime: number | undefined): void { + try { + span.end(endTime === undefined ? undefined : { endTime }); + } catch (error) { + logInstrumentationError("Eve span end", error); + } +} + +function logInstrumentationError(label: string, error: unknown): void { + debugLogger.warn(`Error in ${label} instrumentation:`, error); +} diff --git a/js/src/vendor-sdk-types/eve.ts b/js/src/vendor-sdk-types/eve.ts new file mode 100644 index 000000000..4d6decfbb --- /dev/null +++ b/js/src/vendor-sdk-types/eve.ts @@ -0,0 +1,257 @@ +/** + * Vendored types for eve's authored hook APIs. + * + * Keep this surface intentionally narrow. These types are not exported to SDK + * users and should only cover fields we read, correlate, or log. + */ + +export type EveJsonValue = + | null + | boolean + | number + | string + | EveJsonValue[] + | { readonly [key: string]: EveJsonValue }; + +export type EveJsonObject = { readonly [key: string]: EveJsonValue }; + +export interface EveHookContext { + readonly agent?: { + readonly name?: string; + readonly nodeId?: string; + }; + readonly channel?: { + readonly kind?: string; + readonly continuationToken?: string; + }; + readonly session?: { + readonly id?: string; + }; +} + +export type EveAssistantStepFinishReason = + | "content-filter" + | "error" + | "length" + | "other" + | "stop" + | "tool-calls"; + +export interface EveStreamEventMeta { + readonly at: string; +} + +export interface EveRuntimeToolCallActionRequest { + readonly callId: string; + readonly input: EveJsonObject; + readonly kind: "tool-call"; + readonly toolName: string; +} + +export interface EveRuntimeToolResultActionResult { + readonly callId: string; + readonly isError?: boolean; + readonly kind: "tool-result"; + readonly output: EveJsonValue; + readonly toolName: string; +} + +export type EveRuntimeActionRequest = + | EveRuntimeToolCallActionRequest + | { + readonly callId: string; + readonly input?: EveJsonObject; + readonly kind: "load-skill" | "remote-agent-call" | "subagent-call"; + readonly name?: string; + }; + +export type EveRuntimeActionResult = + | EveRuntimeToolResultActionResult + | { + readonly callId: string; + readonly isError?: boolean; + readonly kind: "load-skill-result" | "subagent-result"; + readonly output?: EveJsonValue; + readonly name?: string; + readonly subagentName?: string; + }; + +export type EveActionResultStatus = "completed" | "failed" | "rejected"; + +export interface EveActionResultError { + readonly code: string; + readonly message: string; +} + +export type EveHandleMessageStreamEvent = + | { + readonly data: { + readonly invocation?: unknown; + readonly runtime?: { + readonly agentId: string; + readonly agentName?: string; + readonly eveVersion: string; + readonly modelId: string; + }; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "session.started"; + } + | { + readonly data: { + readonly sequence: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "turn.started"; + } + | { + readonly data: { + readonly sequence: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "turn.completed"; + } + | { + readonly data: { + readonly message: string; + readonly sequence: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "message.received"; + } + | { + readonly data: { + readonly finishReason: EveAssistantStepFinishReason; + readonly message: string | null; + readonly sequence: number; + readonly stepIndex: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "message.completed"; + } + | { + readonly data: { + readonly result: EveJsonValue; + readonly sequence: number; + readonly stepIndex: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "result.completed"; + } + | { + readonly data: { + readonly sequence: number; + readonly stepIndex: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "step.started"; + } + | { + readonly data: { + readonly finishReason: EveAssistantStepFinishReason; + readonly providerMetadata?: { + readonly gateway?: { + readonly generationId?: string; + }; + }; + readonly sequence: number; + readonly stepIndex: number; + readonly turnId: string; + readonly usage?: { + readonly cacheReadTokens?: number; + readonly cacheWriteTokens?: number; + readonly costUsd?: number; + readonly inputTokens?: number; + readonly outputTokens?: number; + }; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "step.completed"; + } + | { + readonly data: { + readonly code: string; + readonly details?: EveJsonObject; + readonly message: string; + readonly sequence: number; + readonly stepIndex: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "step.failed"; + } + | { + readonly data: { + readonly actions: readonly EveRuntimeActionRequest[]; + readonly sequence: number; + readonly stepIndex: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "actions.requested"; + } + | { + readonly data: { + readonly error?: EveActionResultError; + readonly result: EveRuntimeActionResult; + readonly sequence: number; + readonly stepIndex: number; + readonly status: EveActionResultStatus; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "action.result"; + } + | { + readonly data: { + readonly code: string; + readonly details?: EveJsonObject; + readonly message: string; + readonly sequence: number; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "turn.failed"; + } + | { + readonly data: { + readonly code: string; + readonly details?: EveJsonObject; + readonly message: string; + readonly sessionId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "session.failed"; + } + | { + readonly data: { + readonly wait: "next-user-message"; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "session.waiting"; + } + | { + readonly meta?: EveStreamEventMeta; + readonly type: "session.completed"; + }; + +export interface EveHookDefinition { + readonly events?: { + readonly "*"?: ( + event: EveHandleMessageStreamEvent, + ctx: EveHookContext, + ) => void | Promise; + readonly [eventType: string]: + | (( + event: EveHandleMessageStreamEvent, + ctx: EveHookContext, + ) => void | Promise) + | undefined; + }; +} From 32203327f156d4e29afe90fa38769de0c9aa7fb2 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 6 Jul 2026 21:05:04 +0200 Subject: [PATCH 02/10] record e2e tests --- .../eve-instrumentation.span-tree.json | 24 ++++++--- .../eve-instrumentation.span-tree.txt | 24 ++++++--- .../agent/hooks/braintrust.ts | 11 +++- .../plugins/eve-plugin.test.ts | 11 +++- js/src/instrumentation/plugins/eve-plugin.ts | 53 +++++++++++++------ 5 files changed, 92 insertions(+), 31 deletions(-) diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json index 06aad68d8..a6efff06c 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -40,7 +40,9 @@ "eve.turn.id": "", "eve.turn.sequence": 0, "model": "gpt-5.4-mini", - "provider": "openai" + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" }, "metrics": { "completion_tokens": 26, @@ -70,7 +72,9 @@ "eve.tool.call_id": "", "eve.tool.name": "search", "eve.turn.id": "", - "eve.turn.sequence": 0 + "eve.turn.sequence": 0, + "scenario": "eve-instrumentation", + "testRunId": "" } }, { @@ -133,7 +137,9 @@ "eve.turn.id": "", "eve.turn.sequence": 0, "model": "gpt-5.4-mini", - "provider": "openai" + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" }, "metrics": { "completion_tokens": 27, @@ -164,7 +170,9 @@ "eve.tool.call_id": "", "eve.tool.name": "read", "eve.turn.id": "", - "eve.turn.sequence": 0 + "eve.turn.sequence": 0, + "scenario": "eve-instrumentation", + "testRunId": "" } }, { @@ -243,7 +251,9 @@ "eve.turn.id": "", "eve.turn.sequence": 0, "model": "gpt-5.4-mini", - "provider": "openai" + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" }, "metrics": { "completion_tokens": 40, @@ -268,7 +278,9 @@ "eve.turn.id": "", "eve.turn.sequence": 0, "model": "gpt-5.4-mini", - "provider": "openai" + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" }, "metrics": { "completion_tokens": 93, diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt index f6bfa4a24..7c22627b6 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -15,7 +15,9 @@ span_tree: "eve.turn.id": "", "eve.turn.sequence": 0, "model": "gpt-5.4-mini", - "provider": "openai" + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" } metrics: { "completion_tokens": 93, @@ -56,7 +58,9 @@ span_tree: │ "eve.turn.id": "", │ "eve.turn.sequence": 0, │ "model": "gpt-5.4-mini", - │ "provider": "openai" + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" │ } │ metrics: { │ "completion_tokens": 26, @@ -82,7 +86,9 @@ span_tree: │ "eve.tool.call_id": "", │ "eve.tool.name": "search", │ "eve.turn.id": "", - │ "eve.turn.sequence": 0 + │ "eve.turn.sequence": 0, + │ "scenario": "eve-instrumentation", + │ "testRunId": "" │ } ├── eve.step [llm] │ input: [ @@ -141,7 +147,9 @@ span_tree: │ "eve.turn.id": "", │ "eve.turn.sequence": 0, │ "model": "gpt-5.4-mini", - │ "provider": "openai" + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" │ } │ metrics: { │ "completion_tokens": 27, @@ -168,7 +176,9 @@ span_tree: │ "eve.tool.call_id": "", │ "eve.tool.name": "read", │ "eve.turn.id": "", - │ "eve.turn.sequence": 0 + │ "eve.turn.sequence": 0, + │ "scenario": "eve-instrumentation", + │ "testRunId": "" │ } └── eve.step [llm] input: [ @@ -243,7 +253,9 @@ span_tree: "eve.turn.id": "", "eve.turn.sequence": 0, "model": "gpt-5.4-mini", - "provider": "openai" + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" } metrics: { "completion_tokens": 40, diff --git a/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts b/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts index 4b0d8c56e..2597dc318 100644 --- a/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts +++ b/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts @@ -1,4 +1,13 @@ import { braintrustEveHook } from "braintrust"; import { defineHook } from "eve/hooks"; -export default defineHook(braintrustEveHook()); +export default defineHook( + braintrustEveHook({ + metadata: { + scenario: "eve-instrumentation", + ...(process.env.BRAINTRUST_E2E_RUN_ID + ? { testRunId: process.env.BRAINTRUST_E2E_RUN_ID } + : {}), + }, + }), +); diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index f10951c19..7acbfd685 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -44,7 +44,12 @@ describe("braintrustEveHook", () => { }); it("records a flat Eve turn with session model metadata", async () => { - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", + }, + }).events?.["*"]; expect(wildcard).toBeDefined(); const ctx: EveHookContext = { @@ -208,6 +213,8 @@ describe("braintrustEveHook", () => { "eve.channel.kind": "http", "eve.session.id": "session-flat-tree", "eve.turn.id": "turn-flat-tree", + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", }, metrics: { completion_tokens: 13, @@ -240,6 +247,8 @@ describe("braintrustEveHook", () => { expect(step.metadata).toMatchObject({ ...expectedModelMetadata, "eve.step.index": index, + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", }); } expect(steps[0]?.input).toEqual([ diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index 4883631ca..e460daccb 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -45,11 +45,13 @@ type ToolState = SpanState & { const EVE_BRIDGE = Symbol.for("braintrust.eve.bridge"); /** Manual hook instrumentation for eve runtime stream events. */ -export function braintrustEveHook(): EveHookDefinition { +export function braintrustEveHook( + options: { metadata?: Record } = {}, +): EveHookDefinition { return { events: { "*": (event: EveHandleMessageStreamEvent, ctx: EveHookContext) => { - getEveBridge().handle(event, ctx); + getEveBridge().handle(event, ctx, options.metadata); }, }, }; @@ -73,31 +75,39 @@ class EveBridge { private toolsByCallKey = new Map(); private turnsByKey = new Map(); - handle(event: unknown, ctx: unknown): void { + handle( + event: unknown, + ctx: unknown, + hookMetadata?: Record, + ): void { if (!isObject(event)) { return; } try { - this.handleEvent(event as EveHandleMessageStreamEvent, ctx); + this.handleEvent(event as EveHandleMessageStreamEvent, ctx, hookMetadata); } catch (error) { logInstrumentationError("Eve hook event", error); } } - private handleEvent(event: EveHandleMessageStreamEvent, ctx: unknown): void { + private handleEvent( + event: EveHandleMessageStreamEvent, + ctx: unknown, + hookMetadata?: Record, + ): void { switch (event.type) { case "session.started": this.handleSessionStarted(event, ctx); return; case "turn.started": - this.handleTurnStarted(event, ctx); + this.handleTurnStarted(event, ctx, hookMetadata); return; case "message.received": - this.handleMessageReceived(event, ctx); + this.handleMessageReceived(event, ctx, hookMetadata); return; case "step.started": - this.handleStepStarted(event, ctx); + this.handleStepStarted(event, ctx, hookMetadata); return; case "message.completed": this.handleMessageCompleted(event, ctx); @@ -106,10 +116,10 @@ class EveBridge { this.handleResultCompleted(event, ctx); return; case "actions.requested": - this.handleActionsRequested(event, ctx); + this.handleActionsRequested(event, ctx, hookMetadata); return; case "action.result": - this.handleActionResult(event, ctx); + this.handleActionResult(event, ctx, hookMetadata); return; case "step.completed": this.handleStepCompleted(event, ctx); @@ -162,6 +172,7 @@ class EveBridge { private handleTurnStarted( event: Extract, ctx: unknown, + hookMetadata?: Record, ): void { const sessionId = sessionIdFromContext(ctx); if (!sessionId) { @@ -169,7 +180,7 @@ class EveBridge { } const key = turnKey(sessionId, event.data.turnId); - const metadata = this.turnMetadata(sessionId, event, ctx); + const metadata = this.turnMetadata(sessionId, event, ctx, hookMetadata); const existing = this.turnsByKey.get(key); if (existing) { existing.metadata = metadata; @@ -197,8 +208,9 @@ class EveBridge { private handleMessageReceived( event: Extract, ctx: unknown, + hookMetadata?: Record, ): void { - const turn = this.ensureTurn(event, ctx); + const turn = this.ensureTurn(event, ctx, hookMetadata); if (!turn) { return; } @@ -214,8 +226,9 @@ class EveBridge { private handleStepStarted( event: Extract, ctx: unknown, + hookMetadata?: Record, ): void { - const turn = this.ensureTurn(event, ctx); + const turn = this.ensureTurn(event, ctx, hookMetadata); if (!turn) { return; } @@ -294,8 +307,9 @@ class EveBridge { private handleActionsRequested( event: Extract, ctx: unknown, + hookMetadata?: Record, ): void { - const turn = this.ensureTurn(event, ctx); + const turn = this.ensureTurn(event, ctx, hookMetadata); const sessionId = sessionIdFromContext(ctx); if (!turn || !sessionId) { return; @@ -368,6 +382,7 @@ class EveBridge { private handleActionResult( event: Extract, ctx: unknown, + hookMetadata?: Record, ): void { if (!isToolResult(event.data.result)) { return; @@ -381,7 +396,7 @@ class EveBridge { const key = toolKey(sessionId, event.data.result.callId); const tool = this.toolsByCallKey.get(key) ?? - this.startSyntheticTool(event, ctx, event.data.result); + this.startSyntheticTool(event, ctx, event.data.result, hookMetadata); if (!tool) { return; } @@ -575,6 +590,7 @@ class EveBridge { } >, ctx: unknown, + hookMetadata?: Record, ): TurnState | undefined { const sessionId = sessionIdFromContext(ctx); if (!sessionId) { @@ -587,7 +603,7 @@ class EveBridge { return existing; } - const metadata = this.turnMetadata(sessionId, event, ctx); + const metadata = this.turnMetadata(sessionId, event, ctx, hookMetadata); const span = startSpan({ event: { metadata }, name: "eve.turn", @@ -636,8 +652,9 @@ class EveBridge { event: Extract, ctx: unknown, result: EveRuntimeToolResultActionResult, + hookMetadata?: Record, ): ToolState | undefined { - const turn = this.ensureTurn(event, ctx); + const turn = this.ensureTurn(event, ctx, hookMetadata); const sessionId = sessionIdFromContext(ctx); if (!turn || !sessionId) { return undefined; @@ -711,6 +728,7 @@ class EveBridge { { data: { readonly sequence: number; readonly turnId: string } } >, ctx: unknown, + hookMetadata?: Record, ): Record { return { ...baseMetadata({ @@ -719,6 +737,7 @@ class EveBridge { turnId: event.data.turnId, turnSequence: event.data.sequence, }), + ...(hookMetadata ?? {}), ...agentMetadataFromContext(ctx), ...(this.sessionsById.get(sessionId)?.metadata ?? {}), }; From d969b198d9afbd3e72307d583f76444bc84159ec Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Jul 2026 14:05:40 +0200 Subject: [PATCH 03/10] fix --- .../plugins/eve-plugin.test.ts | 162 ++++++++++++++++++ js/src/instrumentation/plugins/eve-plugin.ts | 92 +++++++--- 2 files changed, 226 insertions(+), 28 deletions(-) diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index 7acbfd685..6e7bf26b1 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -317,6 +317,168 @@ describe("braintrustEveHook", () => { }); }); + it("merges incremental tool call requests into one assistant message", async () => { + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { + agent: { name: "eve-test-agent" }, + channel: { kind: "http" }, + session: { id: "session-incremental-tools" }, + }; + const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); + + emit({ + data: { sequence: 0, turnId: "turn-incremental-tools" }, + type: "turn.started", + }); + emit({ + data: { + message: "Search then read", + sequence: 0, + turnId: "turn-incremental-tools", + }, + type: "message.received", + }); + emit({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-incremental-tools" }, + type: "step.started", + }); + emit({ + data: { + actions: [ + { + callId: "call-search", + input: { query: "Eve instrumentation" }, + kind: "tool-call", + toolName: "search", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-incremental-tools", + }, + type: "actions.requested", + }); + emit({ + data: { + actions: [ + { + callId: "call-read", + input: { url: "https://eve.dev/docs/guides/instrumentation" }, + kind: "tool-call", + toolName: "read", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-incremental-tools", + }, + type: "actions.requested", + }); + emit({ + data: { + result: { + callId: "call-search", + kind: "tool-result", + output: { url: "https://eve.dev/docs/guides/instrumentation" }, + toolName: "search", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-incremental-tools", + }, + type: "action.result", + }); + emit({ + data: { + result: { + callId: "call-read", + kind: "tool-result", + output: { excerpt: "Eve hooks expose runtime stream events." }, + toolName: "read", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-incremental-tools", + }, + type: "action.result", + }); + emit({ + data: { + finishReason: "tool-calls", + sequence: 0, + stepIndex: 0, + turnId: "turn-incremental-tools", + }, + type: "step.completed", + }); + emit({ + data: { sequence: 0, stepIndex: 1, turnId: "turn-incremental-tools" }, + type: "step.started", + }); + emit({ + data: { + finishReason: "stop", + message: "Done.", + sequence: 0, + stepIndex: 1, + turnId: "turn-incremental-tools", + }, + type: "message.completed", + }); + emit({ + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 1, + turnId: "turn-incremental-tools", + }, + type: "step.completed", + }); + emit({ + data: { sequence: 0, turnId: "turn-incremental-tools" }, + type: "turn.completed", + }); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const steps = spans + .filter((span) => + String(span.span_attributes?.name).startsWith("eve.step"), + ) + .sort( + (left, right) => + Number(left.metadata?.["eve.step.index"]) - + Number(right.metadata?.["eve.step.index"]), + ); + + expect(steps[0]?.output).toMatchObject({ + message: { + tool_calls: [ + { id: "call-search", function: { name: "search" } }, + { id: "call-read", function: { name: "read" } }, + ], + }, + }); + const assistantToolMessages = steps[1]?.input.filter( + (message: Record) => + message.role === "assistant" && Array.isArray(message.tool_calls), + ); + expect(assistantToolMessages).toHaveLength(1); + expect(assistantToolMessages?.[0]).toMatchObject({ + content: null, + role: "assistant", + tool_calls: [ + { id: "call-search", function: { name: "search" }, type: "function" }, + { id: "call-read", function: { name: "read" }, type: "function" }, + ], + }); + }); + it("does not throw when Eve emits malformed events or failures", async () => { const wildcard = braintrustEveHook().events?.["*"]; expect(wildcard).toBeDefined(); diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index e460daccb..5b7b0bd4e 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -16,17 +16,26 @@ type SpanState = { span: Span; }; +type ToolCall = { + function: { + arguments: string; + name: string; + }; + id: string; + type: "function"; +}; + +type AssistantToolCallMessage = { + content: unknown; + role: "assistant"; + tool_calls: ToolCall[]; +}; + type StepState = SpanState & { + assistantToolCallMessage?: AssistantToolCallMessage; metrics: Record; output?: unknown; - toolCalls: Array<{ - function: { - arguments: string; - name: string; - }; - id: string; - type: "function"; - }>; + toolCalls: ToolCall[]; }; type TurnState = SpanState & { @@ -278,6 +287,10 @@ class EveBridge { : {}), }, }; + if (step.assistantToolCallMessage) { + step.assistantToolCallMessage.content = event.data.message; + step.assistantToolCallMessage.tool_calls = [...step.toolCalls]; + } const turn = this.turnForEvent(event, ctx); if (turn && event.data.finishReason !== "tool-calls") { @@ -321,28 +334,11 @@ class EveBridge { return; } - turn.messages.push({ - content: null, - role: "assistant", - tool_calls: toolActions.map(openAIToolCallFromAction), - }); - for (const action of toolActions) { - step?.toolCalls.push(openAIToolCallFromAction(action)); if (step) { - if (step.output && isObject(step.output)) { - const message = Reflect.get(step.output, "message"); - if (isObject(message)) { - Reflect.set(message, "tool_calls", [...step.toolCalls]); - } - } else { - step.output = { - message: { - content: null, - role: "assistant", - tool_calls: [...step.toolCalls], - }, - }; + const toolCall = openAIToolCallFromAction(action); + if (!step.toolCalls.some((existing) => existing.id === toolCall.id)) { + step.toolCalls.push(toolCall); } } @@ -377,6 +373,46 @@ class EveBridge { turnKey: turnKey(sessionId, event.data.turnId), }); } + + if (!step) { + turn.messages.push({ + content: null, + role: "assistant", + tool_calls: toolActions.map(openAIToolCallFromAction), + }); + return; + } + + const outputMessage = + step.output && isObject(step.output) + ? Reflect.get(step.output, "message") + : undefined; + const content = isObject(outputMessage) + ? Reflect.get(outputMessage, "content") + : null; + if (!step.assistantToolCallMessage) { + step.assistantToolCallMessage = { + content, + role: "assistant", + tool_calls: [...step.toolCalls], + }; + turn.messages.push(step.assistantToolCallMessage); + } else { + step.assistantToolCallMessage.content = content; + step.assistantToolCallMessage.tool_calls = [...step.toolCalls]; + } + + if (isObject(outputMessage)) { + Reflect.set(outputMessage, "tool_calls", [...step.toolCalls]); + } else { + step.output = { + message: { + content, + role: "assistant", + tool_calls: [...step.toolCalls], + }, + }; + } } private handleActionResult( From c914a5b8fea102138d7ffd56b2d36ee496908ab6 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 9 Jul 2026 17:55:53 +0200 Subject: [PATCH 04/10] workflow persistence --- .../__cassettes__/eve-v0-20-0.cassette.json | 1119 +++++++++++++-- .../eve-instrumentation.span-tree.json | 351 +++-- .../eve-instrumentation.span-tree.txt | 331 +++-- .../eve-instrumentation/agent/instructions.md | 11 +- .../agent/subagents/researcher/agent.ts | 15 + .../subagents/researcher/hooks/braintrust.ts | 13 + .../subagents/researcher/instructions.md | 7 + .../subagents/researcher/tools/search.ts | 16 + .../eve-instrumentation/agent/tools/read.ts | 2 +- .../eve-instrumentation/scenario.test.ts | 69 +- e2e/scenarios/eve-instrumentation/scenario.ts | 25 +- .../plugins/eve-plugin.test.ts | 602 ++++++-- js/src/instrumentation/plugins/eve-plugin.ts | 1224 ++++++++++++----- js/src/vendor-sdk-types/eve.ts | 54 +- 14 files changed, 2999 insertions(+), 840 deletions(-) create mode 100644 e2e/scenarios/eve-instrumentation/agent/subagents/researcher/agent.ts create mode 100644 e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts create mode 100644 e2e/scenarios/eve-instrumentation/agent/subagents/researcher/instructions.md create mode 100644 e2e/scenarios/eve-instrumentation/agent/subagents/researcher/tools/search.ts diff --git a/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json b/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json index 3e0f496b7..e39ea21af 100644 --- a/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json +++ b/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json @@ -2,9 +2,9 @@ "entries": [ { "callIndex": 0, - "id": "63740c316833eadb", + "id": "b18dba30bbb5cd34", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-06T12:42:40.161Z", + "recordedAt": "2026-07-09T11:20:16.531Z", "request": { "body": { "kind": "json", @@ -12,7 +12,7 @@ "include": ["web_search_call.action.sources"], "input": [ { - "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the search tool exactly once with the full user message as the query.\n2. After the search result is available, call the read tool exactly once with the\n URL returned by search.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the search title, URL, and read\n excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", "role": "developer" }, { @@ -325,6 +325,813 @@ }, "type": "function" }, + { + "description": "Research the Eve instrumentation documentation before the parent reads it.", + "name": "researcher", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83ef76a081a188d35d3e91cf3b1d\",\"object\":\"response\",\"created_at\":1783596015,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83ef76a081a188d35d3e91cf3b1d\",\"object\":\"response\",\"created_at\":1783596015,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_UiWXOc9U8IcpmtTc8pwI68SC\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"r06Lt7wN2uigNU\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"message\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"kfp6duqAO\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"DJeTPH0SmOA30\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"xDPyBL4HXu3rH\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"XpzaRbFEXP2l\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"x1OoIccaee\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"LKBIzXmUY4g\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"YnbcwnaVRE2d\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"AzuidOp6Vzi5ti\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"Y1rEv3yfvbiIiu8\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"6oF5AEuOnKTEaTR\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"txOnYRU\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"0RK2NLvIilXvUf\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_UiWXOc9U8IcpmtTc8pwI68SC\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":18}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83ef76a081a188d35d3e91cf3b1d\",\"object\":\"response\",\"created_at\":1783596015,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596016,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_UiWXOc9U8IcpmtTc8pwI68SC\",\"name\":\"researcher\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6666,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":28,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6694},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a186f0337abf4baf-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Thu, 09 Jul 2026 11:20:15 GMT", + "openai-processing-ms": "413", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992692", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "1e202202-8520-470e-aaca-0a68a2bd4d5a" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 1, + "id": "46596331f4831e14", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-09T11:20:17.751Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f0e54481a3b626579ce09833cd\",\"object\":\"response\",\"created_at\":1783596016,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f0e54481a3b626579ce09833cd\",\"object\":\"response\",\"created_at\":1783596016,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_bsPk0zM9ENF7o5XXNuEmMjhA\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"4vtnIIO7ut7BE8\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"query\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"HwxIxM3Vnkw\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"7ttA2p4MMqYUq\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"aGkGxnSf3goW0\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"jHLbTBVTHjQW\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"efEEbcQutQ\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"qCoSFZDB4aR\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"nBI80OUUZxbm\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"glsQVaBZXgtYGX\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"VriyQtUYoi9mRp4\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"EgYqpoUKz19UNAw\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"Yz4DPVG\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"1OIerAy9poSd7e\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_bsPk0zM9ENF7o5XXNuEmMjhA\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":18}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f0e54481a3b626579ce09833cd\",\"object\":\"response\",\"created_at\":1783596016,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596017,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_bsPk0zM9ENF7o5XXNuEmMjhA\",\"name\":\"search\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6554,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":5504},\"output_tokens\":26,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6580},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a186f040bc704baf-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Thu, 09 Jul 2026 11:20:17 GMT", + "openai-processing-ms": "330", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992806", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "d8fbf635-d64b-4692-a655-8facd30110ef" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 2, + "id": "7e6e893cf4f62b3b", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-09T11:20:18.923Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16", + "type": "item_reference" + }, + { + "call_id": "call_bsPk0zM9ENF7o5XXNuEmMjhA", + "output": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, { "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", "name": "agent", @@ -355,26 +1162,32 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2bed894819f941441a0e61d48b8\",\"object\":\"response\",\"created_at\":1783341758,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2bed894819f941441a0e61d48b8\",\"object\":\"response\",\"created_at\":1783341758,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_OJGkAZ5ouXaH7NjuUH2mSbk6\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"XnL4BuCUjYo2Ga\",\"output_index\":0,\"sequence_number\":3}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"query\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"l6rkWWYxmHg\",\"output_index\":0,\"sequence_number\":4}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"GymIr2w2XZVpA\",\"output_index\":0,\"sequence_number\":5}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"OwgJorNkY8F7z\",\"output_index\":0,\"sequence_number\":6}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"zI4Q0bRZ70XL\",\"output_index\":0,\"sequence_number\":7}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"1mvqmuEoZG\",\"output_index\":0,\"sequence_number\":8}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"7LdGHqhYJm5\",\"output_index\":0,\"sequence_number\":9}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"SUjuR9onJ7HP\",\"output_index\":0,\"sequence_number\":10}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"9vSg1jlhKVxvd4\",\"output_index\":0,\"sequence_number\":12}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"lkED6zGdV426rzv\",\"output_index\":0,\"sequence_number\":13}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"7ZbUxm5q3VLOlzU\",\"output_index\":0,\"sequence_number\":14}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"fjhDwnd\",\"output_index\":0,\"sequence_number\":15}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"obfuscation\":\"rtm0Kzc5YO8U7T\",\"output_index\":0,\"sequence_number\":16}", - "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"output_index\":0,\"sequence_number\":17}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_OJGkAZ5ouXaH7NjuUH2mSbk6\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":18}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2bed894819f941441a0e61d48b8\",\"object\":\"response\",\"created_at\":1783341758,\"status\":\"completed\",\"background\":false,\"completed_at\":1783341760,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_OJGkAZ5ouXaH7NjuUH2mSbk6\",\"name\":\"search\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6564,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":26,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6590},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f2069c81a39c90caedbc0c9352\",\"object\":\"response\",\"created_at\":1783596018,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f2069c81a39c90caedbc0c9352\",\"object\":\"response\",\"created_at\":1783596018,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Research\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"JRipkwD1\",\"output_index\":0,\"sequence_number\":4}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"er\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"atTZhdzsScalqm\",\"output_index\":0,\"sequence_number\":5}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"zrRLUJyXe\",\"output_index\":0,\"sequence_number\":6}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"wmyhP0radRs3MKw\",\"output_index\":0,\"sequence_number\":7}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"yLjwf9XNRjnw\",\"output_index\":0,\"sequence_number\":8}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":9}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"KwwyoCmDJDcZA1\",\"output_index\":0,\"sequence_number\":10}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"JB6XrqKLc7\",\"output_index\":0,\"sequence_number\":11}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"ujU5csr5noh8x\",\"output_index\":0,\"sequence_number\":12}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"QgORFdpecykmJ\",\"output_index\":0,\"sequence_number\":13}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"eCwnNNzfBNUH\",\"output_index\":0,\"sequence_number\":14}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"JVqGWcybjOW\",\"output_index\":0,\"sequence_number\":15}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"OnWFi9JsgYqgiM\",\"output_index\":0,\"sequence_number\":16}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"j3olh3YhMHynn\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"3ExmkOHn9MkYTt\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"K2I9nTixsLfGb\",\"output_index\":0,\"sequence_number\":19}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"KMxU28uX\",\"output_index\":0,\"sequence_number\":20}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"0GvouO2FICn\",\"output_index\":0,\"sequence_number\":21}", + "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":22,\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}", + "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"},\"sequence_number\":23}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":24}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f2069c81a39c90caedbc0c9352\",\"object\":\"response\",\"created_at\":1783596018,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596018,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6624,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":22,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6646},\"user\":null,\"metadata\":{}},\"sequence_number\":25}" ], "kind": "sse" }, @@ -382,11 +1195,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a16eb0c61b905b89-VIE", + "cf-ray": "a186f047da804baf-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Mon, 06 Jul 2026 12:42:39 GMT", - "openai-processing-ms": "301", + "date": "Thu, 09 Jul 2026 11:20:18 GMT", + "openai-processing-ms": "340", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -396,20 +1209,20 @@ "x-ratelimit-limit-requests": "30000", "x-ratelimit-limit-tokens": "180000000", "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "179992794", + "x-ratelimit-remaining-tokens": "179992734", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "c268729b-cdae-494b-a9f6-d154a9096847" + "x-request-id": "bc2a090f-5ed2-4514-9bee-89b1a76aa13a" }, "status": 200, "statusText": "OK" } }, { - "callIndex": 1, - "id": "6eaa461531ba23e9", + "callIndex": 3, + "id": "70155a3202ad7b6a", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-06T12:42:41.865Z", + "recordedAt": "2026-07-09T11:20:20.187Z", "request": { "body": { "kind": "json", @@ -417,7 +1230,7 @@ "include": ["web_search_call.action.sources"], "input": [ { - "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the search tool exactly once with the full user message as the query.\n2. After the search result is available, call the read tool exactly once with the\n URL returned by search.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the search title, URL, and read\n excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", "role": "developer" }, { @@ -430,12 +1243,12 @@ "role": "user" }, { - "id": "fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea", + "id": "fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324", "type": "item_reference" }, { - "call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6", - "output": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "call_id": "call_UiWXOc9U8IcpmtTc8pwI68SC", + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "type": "function_call_output" } ], @@ -739,6 +1552,26 @@ }, "type": "function" }, + { + "description": "Research the Eve instrumentation documentation before the parent reads it.", + "name": "researcher", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + }, { "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", "name": "agent", @@ -769,27 +1602,27 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c082ac819fa0b53db4088f4b4b\",\"object\":\"response\",\"created_at\":1783341760,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c082ac819fa0b53db4088f4b4b\",\"object\":\"response\",\"created_at\":1783341760,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_kZ7AzZ9mCqataCzrSSLxRkFh\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"hCZj3eURcxA5u7\",\"output_index\":0,\"sequence_number\":3}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"url\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"YTahm2y9wc2a2\",\"output_index\":0,\"sequence_number\":4}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"O9KdvSTHznEA4\",\"output_index\":0,\"sequence_number\":5}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"https\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"cZXggUplvDp\",\"output_index\":0,\"sequence_number\":6}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"://\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"W1IPVLgHRdfnK\",\"output_index\":0,\"sequence_number\":7}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"eve\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"IlAAdBPloI7ZI\",\"output_index\":0,\"sequence_number\":8}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\".dev\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"vuTEb7koKwkU\",\"output_index\":0,\"sequence_number\":9}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/docs\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"3tkcCXVFaVS\",\"output_index\":0,\"sequence_number\":10}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/g\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"Fnb0kAAbyEFxmo\",\"output_index\":0,\"sequence_number\":11}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"uid\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"F53YeZTZPgt2h\",\"output_index\":0,\"sequence_number\":12}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"es\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"wc5Z1NagkfGlho\",\"output_index\":0,\"sequence_number\":13}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/in\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"YVqWvIHZvcnXv\",\"output_index\":0,\"sequence_number\":14}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"strument\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"9e6Bnngc\",\"output_index\":0,\"sequence_number\":15}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"ation\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"5nPU7ASnvsR\",\"output_index\":0,\"sequence_number\":16}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"obfuscation\":\"tYrx4WFsbNsUju\",\"output_index\":0,\"sequence_number\":17}", - "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"item_id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"output_index\":0,\"sequence_number\":18}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_kZ7AzZ9mCqataCzrSSLxRkFh\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":19}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c082ac819fa0b53db4088f4b4b\",\"object\":\"response\",\"created_at\":1783341760,\"status\":\"completed\",\"background\":false,\"completed_at\":1783341761,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_kZ7AzZ9mCqataCzrSSLxRkFh\",\"name\":\"read\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6634,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6661},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f3712c81a1a25e232396ef7f18\",\"object\":\"response\",\"created_at\":1783596019,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f3712c81a1a25e232396ef7f18\",\"object\":\"response\",\"created_at\":1783596019,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_yRc6f1bJPthZhaIEzCySgUi0\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"3yEZTEfKtX2uPH\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"url\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"pViqlsbdLm9Va\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"JoC4t2F0ZjA0j\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"https\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"3SIyr1G934J\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"://\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"fb8FNgLM9KRva\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"eve\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"DsXhS39pakx6S\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\".dev\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"TahVz5KKQdux\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/docs\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"CbI5smSCsQC\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/g\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"sHqF3zJ3ylLC6D\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"uid\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"FrulpXK3ohZuf\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"es\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"NyjUoWGOGsI9LO\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/in\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"q1r4ncVF0n7XZ\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"strument\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"u2TPRcWH\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"ation\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"iBlTApKlyB4\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"Db3HDqB94PQlS8\",\"output_index\":0,\"sequence_number\":17}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_yRc6f1bJPthZhaIEzCySgUi0\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":19}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f3712c81a1a25e232396ef7f18\",\"object\":\"response\",\"created_at\":1783596019,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596020,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_yRc6f1bJPthZhaIEzCySgUi0\",\"name\":\"read\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6724,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6751},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" ], "kind": "sse" }, @@ -797,11 +1630,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a16eb0d248325b89-VIE", + "cf-ray": "a186f050ba454baf-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Mon, 06 Jul 2026 12:42:41 GMT", - "openai-processing-ms": "505", + "date": "Thu, 09 Jul 2026 11:20:19 GMT", + "openai-processing-ms": "288", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -811,20 +1644,20 @@ "x-ratelimit-limit-requests": "30000", "x-ratelimit-limit-tokens": "180000000", "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "179992725", + "x-ratelimit-remaining-tokens": "179992635", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "2a2e63ed-abb5-4d63-9732-9e1203d43cd5" + "x-request-id": "272b03f6-cb9b-492a-bf56-58cf5cf71805" }, "status": 200, "statusText": "OK" } }, { - "callIndex": 2, - "id": "4b05a20680820f51", + "callIndex": 4, + "id": "c8400420bcabd841", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-06T12:42:43.115Z", + "recordedAt": "2026-07-09T11:20:21.921Z", "request": { "body": { "kind": "json", @@ -832,7 +1665,7 @@ "include": ["web_search_call.action.sources"], "input": [ { - "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the search tool exactly once with the full user message as the query.\n2. After the search result is available, call the read tool exactly once with the\n URL returned by search.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the search title, URL, and read\n excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", "role": "developer" }, { @@ -845,21 +1678,21 @@ "role": "user" }, { - "id": "fc_0fea08d8f9bfc463006a4ba2bfcbe0819fb89c3fb8976a78ea", + "id": "fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324", "type": "item_reference" }, { - "call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6", - "output": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "call_id": "call_UiWXOc9U8IcpmtTc8pwI68SC", + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "type": "function_call_output" }, { - "id": "fc_0fea08d8f9bfc463006a4ba2c1380c819f949f689dbcdeda46", + "id": "fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb", "type": "item_reference" }, { - "call_id": "call_kZ7AzZ9mCqataCzrSSLxRkFh", - "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "call_id": "call_yRc6f1bJPthZhaIEzCySgUi0", + "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", "type": "function_call_output" } ], @@ -1163,6 +1996,26 @@ }, "type": "function" }, + { + "description": "Research the Eve instrumentation documentation before the parent reads it.", + "name": "researcher", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + }, { "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", "name": "agent", @@ -1193,50 +2046,76 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c24358819fb60d53217b74c525\",\"object\":\"response\",\"created_at\":1783341762,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c24358819fb60d53217b74c525\",\"object\":\"response\",\"created_at\":1783341762,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Final\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ANAxDak0zuB\",\"output_index\":0,\"sequence_number\":4}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" answer\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"A7UjBuGVk\",\"output_index\":0,\"sequence_number\":5}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" from\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"yeX5bNWtb13\",\"output_index\":0,\"sequence_number\":6}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"LZJe1rtmYvV\",\"output_index\":0,\"sequence_number\":7}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ubVMHAlNIlruqHa\",\"output_index\":0,\"sequence_number\":8}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"jNlbYXRxMdDm\",\"output_index\":0,\"sequence_number\":9}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":10}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"r53TzX4FOBazqH\",\"output_index\":0,\"sequence_number\":11}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"DO7NjjcMxI\",\"output_index\":0,\"sequence_number\":12}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ICOb1qKdbbUrP\",\"output_index\":0,\"sequence_number\":13}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"SaQSy8Bew6PYA\",\"output_index\":0,\"sequence_number\":14}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zMGbfUgBaedB\",\"output_index\":0,\"sequence_number\":15}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"PRFSTu78jMq\",\"output_index\":0,\"sequence_number\":16}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"TZIiRl9V1OzAAJ\",\"output_index\":0,\"sequence_number\":17}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ztrmEJnKeliLB\",\"output_index\":0,\"sequence_number\":18}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"vJXlAF5E6no9TP\",\"output_index\":0,\"sequence_number\":19}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"2jUQoFtlTAB9n\",\"output_index\":0,\"sequence_number\":20}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"Q0nLWTe1\",\"output_index\":0,\"sequence_number\":21}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"xUWKmOn2oyt\",\"output_index\":0,\"sequence_number\":22}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zNi8pD4u1Ja7L1\",\"output_index\":0,\"sequence_number\":23}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"1Zebv58iK37e\",\"output_index\":0,\"sequence_number\":24}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" hooks\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"S97nFDYp7A\",\"output_index\":0,\"sequence_number\":25}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" expose\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"ZmTvzTqky\",\"output_index\":0,\"sequence_number\":26}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" runtime\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"fB1yh7Uw\",\"output_index\":0,\"sequence_number\":27}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" stream\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zU1vbebdy\",\"output_index\":0,\"sequence_number\":28}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" events\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"gYKd55Lzb\",\"output_index\":0,\"sequence_number\":29}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" that\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"3EZn0CVOJaT\",\"output_index\":0,\"sequence_number\":30}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Brain\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"atGReXeapP\",\"output_index\":0,\"sequence_number\":31}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"trust\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"zWKPFGInVjG\",\"output_index\":0,\"sequence_number\":32}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" maps\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"EeGFqBdfAME\",\"output_index\":0,\"sequence_number\":33}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" into\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"iabXIGBKBpJ\",\"output_index\":0,\"sequence_number\":34}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" a\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"5CXblMcSRN8S5K\",\"output_index\":0,\"sequence_number\":35}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" flat\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"P6u4aXRbe8f\",\"output_index\":0,\"sequence_number\":36}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" turn\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"9P4dWv48mQN\",\"output_index\":0,\"sequence_number\":37}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" trace\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"cZVEaJUhkd\",\"output_index\":0,\"sequence_number\":38}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"obfuscation\":\"qL2yxmkMIJgYbnb\",\"output_index\":0,\"sequence_number\":39}", - "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":40,\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"}", - "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"},\"sequence_number\":41}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":42}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0fea08d8f9bfc463006a4ba2c24358819fb60d53217b74c525\",\"object\":\"response\",\"created_at\":1783341762,\"status\":\"completed\",\"background\":false,\"completed_at\":1783341762,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_0fea08d8f9bfc463006a4ba2c2a888819fb82702a3a6fe95de\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6716,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":40,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6756},\"user\":null,\"metadata\":{}},\"sequence_number\":43}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f480f081a1b815e0368227d112\",\"object\":\"response\",\"created_at\":1783596020,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f480f081a1b815e0368227d112\",\"object\":\"response\",\"created_at\":1783596020,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Final\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"r74GheqwqaP\",\"output_index\":0,\"sequence_number\":4}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" answer\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"4HQwigGY5\",\"output_index\":0,\"sequence_number\":5}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" from\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"mj6cFK9bXZu\",\"output_index\":0,\"sequence_number\":6}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"YU1kGsQAFxF\",\"output_index\":0,\"sequence_number\":7}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"VyD3bRElVYy5hVS\",\"output_index\":0,\"sequence_number\":8}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" researcher\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dcUNX\",\"output_index\":0,\"sequence_number\":9}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"y36dQXOAf\",\"output_index\":0,\"sequence_number\":10}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dUCDqWFMH9nNeWc\",\"output_index\":0,\"sequence_number\":11}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"DxefIcSVNlhd\",\"output_index\":0,\"sequence_number\":12}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":13}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"xaNEi3oa6tIZun\",\"output_index\":0,\"sequence_number\":14}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"jGhFfgVzuD\",\"output_index\":0,\"sequence_number\":15}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"rgZxUElHLdtw5\",\"output_index\":0,\"sequence_number\":16}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"ghBZBaur1QtO1\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dnomYHrR0oSI\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"GWlkQtkzPYZ\",\"output_index\":0,\"sequence_number\":19}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"XqLv5VOKBHp9K4\",\"output_index\":0,\"sequence_number\":20}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"7wNU5sLVHziGM\",\"output_index\":0,\"sequence_number\":21}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"uBTHWUqIs6NZim\",\"output_index\":0,\"sequence_number\":22}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"rvcbhN8JubRdA\",\"output_index\":0,\"sequence_number\":23}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dYsAlUgC\",\"output_index\":0,\"sequence_number\":24}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"cuRVOAP833M\",\"output_index\":0,\"sequence_number\":25}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"NFaKOq55M3sSs9z\",\"output_index\":0,\"sequence_number\":26}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"0fXQ1w4Jx9E\",\"output_index\":0,\"sequence_number\":27}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" title\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"RKZJh0Hc2y\",\"output_index\":0,\"sequence_number\":28}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"DkNDSsDHW2QonsM\",\"output_index\":0,\"sequence_number\":29}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"626NVK4C0REn\",\"output_index\":0,\"sequence_number\":30}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":31}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"jA1iLcrVIooD4Ir\",\"output_index\":0,\"sequence_number\":32}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" URL\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"wBZsmreGiDCV\",\"output_index\":0,\"sequence_number\":33}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"4tH0wvYVeAIM7zl\",\"output_index\":0,\"sequence_number\":34}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"uCUHfUCXLt\",\"output_index\":0,\"sequence_number\":35}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"lExNLsbKFbDw9\",\"output_index\":0,\"sequence_number\":36}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"OYWkA1Xpgb0m6\",\"output_index\":0,\"sequence_number\":37}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"iEGW8PgHSX4R\",\"output_index\":0,\"sequence_number\":38}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"8zo3rbkqnqD\",\"output_index\":0,\"sequence_number\":39}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"FbX61rgYvphVjc\",\"output_index\":0,\"sequence_number\":40}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"Gw4Y5MATJAuPt\",\"output_index\":0,\"sequence_number\":41}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"BjuswyeZzr4omS\",\"output_index\":0,\"sequence_number\":42}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"F9WF9BYyBQVVo\",\"output_index\":0,\"sequence_number\":43}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"ev9OqvcC\",\"output_index\":0,\"sequence_number\":44}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"bkf7PcYT4jx\",\"output_index\":0,\"sequence_number\":45}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"8CJHmcMFXzwswXA\",\"output_index\":0,\"sequence_number\":46}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"pBdt8VPT83o\",\"output_index\":0,\"sequence_number\":47}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" excerpt\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"YxHtlyvo\",\"output_index\":0,\"sequence_number\":48}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"aN8K6HyUNhurZgM\",\"output_index\":0,\"sequence_number\":49}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"mdzLNK6ZsRkB\",\"output_index\":0,\"sequence_number\":50}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" hooks\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"3r7ALtBodX\",\"output_index\":0,\"sequence_number\":51}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" expose\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"yiJireJKY\",\"output_index\":0,\"sequence_number\":52}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" runtime\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"BBb6sLQr\",\"output_index\":0,\"sequence_number\":53}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" stream\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"qN8I4vzpX\",\"output_index\":0,\"sequence_number\":54}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" events\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"MwvgfXvBr\",\"output_index\":0,\"sequence_number\":55}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" that\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"821mE8CX7SP\",\"output_index\":0,\"sequence_number\":56}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Brain\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"XVi4y09KgI\",\"output_index\":0,\"sequence_number\":57}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"trust\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"66I0WlSs1g8\",\"output_index\":0,\"sequence_number\":58}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" maps\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"zEhhyqOT6ih\",\"output_index\":0,\"sequence_number\":59}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" into\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"gSoYjurIcPq\",\"output_index\":0,\"sequence_number\":60}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" a\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"9JRlfxk6q9riK3\",\"output_index\":0,\"sequence_number\":61}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" nested\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"YPUlhuEHa\",\"output_index\":0,\"sequence_number\":62}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" turn\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"LEUvgUYvwOj\",\"output_index\":0,\"sequence_number\":63}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" trace\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"c5x8wjmlF4\",\"output_index\":0,\"sequence_number\":64}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"mTycEutdDgPLFdP\",\"output_index\":0,\"sequence_number\":65}", + "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":66,\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}", + "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"},\"sequence_number\":67}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":68}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f480f081a1b815e0368227d112\",\"object\":\"response\",\"created_at\":1783596020,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596021,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6806,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":66,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6872},\"user\":null,\"metadata\":{}},\"sequence_number\":69}" ], "kind": "sse" }, @@ -1244,11 +2123,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a16eb0dd6acb5b89-VIE", + "cf-ray": "a186f0573f354baf-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Mon, 06 Jul 2026 12:42:42 GMT", - "openai-processing-ms": "277", + "date": "Thu, 09 Jul 2026 11:20:20 GMT", + "openai-processing-ms": "412", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -1258,10 +2137,10 @@ "x-ratelimit-limit-requests": "30000", "x-ratelimit-limit-tokens": "180000000", "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "179992644", + "x-ratelimit-remaining-tokens": "179992551", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "526d60e7-9511-4966-9fc6-ec90737f89a8" + "x-request-id": "e2972c44-de1c-4ae9-9511-52c74e478903" }, "status": 200, "statusText": "OK" diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json index a6efff06c..a3f131a79 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -14,65 +14,185 @@ "role": "user" } ], - "output": { - "message": { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "search" - }, - "id": "", - "type": "function" - } - ] + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "researcher" + }, + "id": "", + "type": "function" + } + ] + } } - }, + ], "metadata": { - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.finish_reason": "tool-calls", - "eve.model.id": "openai/gpt-5.4-mini", - "eve.session.id": "", - "eve.step.index": 0, - "eve.turn.id": "", - "eve.turn.sequence": 0, "model": "gpt-5.4-mini", "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" }, "metrics": { - "completion_tokens": 26, + "completion_tokens": 28, "prompt_cached_tokens": 0, - "prompt_tokens": 6564, - "tokens": 6590 + "prompt_tokens": 6666, + "tokens": 6694 } }, { - "name": "search", + "name": "researcher", "type": "tool", - "children": [], + "children": [ + { + "name": "eve.turn", + "type": "task", + "children": [ + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 26, + "prompt_cached_tokens": 5504, + "prompt_tokens": 6554, + "tokens": 6580 + } + }, + { + "name": "search", + "type": "tool", + "children": [], + "input": { + "query": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "output": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + } + }, + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + }, + { + "content": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "name": "search", + "role": "tool", + "tool_call_id": "call_bsPk0zM9ENF7o5XXNuEmMjhA" + } + ], + "output": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "role": "assistant" + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 22, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6624, + "tokens": 6646 + } + } + ], + "input": [ + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 48, + "prompt_cached_tokens": 12032, + "prompt_tokens": 13178, + "tokens": 13226 + } + } + ], "input": { - "query": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "output": { - "query": "Run the Braintrust Eve instrumentation e2e scenario", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" + "message": "Run the Braintrust Eve instrumentation e2e scenario" }, + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "metadata": { - "eve.action.status": "completed", - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.session.id": "", - "eve.step.index": 0, - "eve.tool.call_id": "", - "eve.tool.name": "search", - "eve.turn.id": "", - "eve.turn.sequence": 0, "scenario": "eve-instrumentation", "testRunId": "" } @@ -92,50 +212,36 @@ "tool_calls": [ { "function": { - "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "search" + "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "researcher" }, "id": "", "type": "function" } ] - }, - { - "content": { - "query": "Run the Braintrust Eve instrumentation e2e scenario", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "name": "search", - "role": "tool", - "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" } ], - "output": { - "message": { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - "name": "read" - }, - "id": "", - "type": "function" - } - ] + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "name": "read" + }, + "id": "", + "type": "function" + } + ] + } } - }, + ], "metadata": { - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.finish_reason": "tool-calls", - "eve.model.id": "openai/gpt-5.4-mini", - "eve.session.id": "", - "eve.step.index": 1, - "eve.turn.id": "", - "eve.turn.sequence": 0, "model": "gpt-5.4-mini", "provider": "openai", "scenario": "eve-instrumentation", @@ -143,9 +249,9 @@ }, "metrics": { "completion_tokens": 27, - "prompt_cached_tokens": 0, - "prompt_tokens": 6634, - "tokens": 6661 + "prompt_cached_tokens": 6528, + "prompt_tokens": 6724, + "tokens": 6751 } }, { @@ -156,21 +262,12 @@ "url": "https://eve.dev/docs/guides/instrumentation" }, "output": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", "section": "Runtime context", "title": "Eve instrumentation", "url": "https://eve.dev/docs/guides/instrumentation" }, "metadata": { - "eve.action.status": "completed", - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.session.id": "", - "eve.step.index": 1, - "eve.tool.call_id": "", - "eve.tool.name": "read", - "eve.turn.id": "", - "eve.turn.sequence": 0, "scenario": "eve-instrumentation", "testRunId": "" } @@ -190,24 +287,14 @@ "tool_calls": [ { "function": { - "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "search" + "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "researcher" }, "id": "", "type": "function" } ] }, - { - "content": { - "query": "Run the Braintrust Eve instrumentation e2e scenario", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "name": "search", - "role": "tool", - "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" - }, { "content": null, "role": "assistant", @@ -223,43 +310,33 @@ ] }, { - "content": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", - "section": "Runtime context", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - }, + "content": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", "name": "read", "role": "tool", - "tool_call_id": "call_kZ7AzZ9mCqataCzrSSLxRkFh" + "tool_call_id": "call_yRc6f1bJPthZhaIEzCySgUi0" } ], - "output": { - "finish_reason": "stop", - "message": { - "content": "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", - "role": "assistant" + "output": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "role": "assistant" + } } - }, + ], "metadata": { - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.finish_reason": "stop", - "eve.model.id": "openai/gpt-5.4-mini", - "eve.session.id": "", - "eve.step.index": 2, - "eve.turn.id": "", - "eve.turn.sequence": 0, "model": "gpt-5.4-mini", "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" }, "metrics": { - "completion_tokens": 40, - "prompt_cached_tokens": 0, - "prompt_tokens": 6716, - "tokens": 6756 + "completion_tokens": 66, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6806, + "tokens": 6872 } } ], @@ -269,24 +346,16 @@ "role": "user" } ], - "output": "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "output": "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", "metadata": { - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.model.id": "openai/gpt-5.4-mini", - "eve.session.id": "", - "eve.turn.id": "", - "eve.turn.sequence": 0, - "model": "gpt-5.4-mini", - "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" }, "metrics": { - "completion_tokens": 93, - "prompt_cached_tokens": 0, - "prompt_tokens": 19914, - "tokens": 20007 + "completion_tokens": 121, + "prompt_cached_tokens": 13056, + "prompt_tokens": 20196, + "tokens": 20317 } } ] diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt index 7c22627b6..0d56dd2aa 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -6,24 +6,16 @@ span_tree: "role": "user" } ] - output: "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace." + output: "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace." metadata: { - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.model.id": "openai/gpt-5.4-mini", - "eve.session.id": "", - "eve.turn.id": "", - "eve.turn.sequence": 0, - "model": "gpt-5.4-mini", - "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" } metrics: { - "completion_tokens": 93, - "prompt_cached_tokens": 0, - "prompt_tokens": 19914, - "tokens": 20007 + "completion_tokens": 121, + "prompt_cached_tokens": 13056, + "prompt_tokens": 20196, + "tokens": 20317 } ├── eve.step [llm] │ input: [ @@ -32,64 +24,166 @@ span_tree: │ "role": "user" │ } │ ] - │ output: { - │ "message": { - │ "content": null, - │ "role": "assistant", - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - │ "name": "search" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] + │ output: [ + │ { + │ "finish_reason": "tool_calls", + │ "index": 0, + │ "message": { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ "name": "researcher" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ } │ } - │ } + │ ] │ metadata: { - │ "eve.agent.name": "eve-instrumentation-scenario", - │ "eve.channel.kind": "http", - │ "eve.finish_reason": "tool-calls", - │ "eve.model.id": "openai/gpt-5.4-mini", - │ "eve.session.id": "", - │ "eve.step.index": 0, - │ "eve.turn.id": "", - │ "eve.turn.sequence": 0, │ "model": "gpt-5.4-mini", │ "provider": "openai", │ "scenario": "eve-instrumentation", │ "testRunId": "" │ } │ metrics: { - │ "completion_tokens": 26, + │ "completion_tokens": 28, │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 6564, - │ "tokens": 6590 + │ "prompt_tokens": 6666, + │ "tokens": 6694 │ } - ├── search [tool] + ├── researcher [tool] │ input: { - │ "query": "Run the Braintrust Eve instrumentation e2e scenario" - │ } - │ output: { - │ "query": "Run the Braintrust Eve instrumentation e2e scenario", - │ "title": "Eve instrumentation", - │ "url": "https://eve.dev/docs/guides/instrumentation" + │ "message": "Run the Braintrust Eve instrumentation e2e scenario" │ } + │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" │ metadata: { - │ "eve.action.status": "completed", - │ "eve.agent.name": "eve-instrumentation-scenario", - │ "eve.channel.kind": "http", - │ "eve.session.id": "", - │ "eve.step.index": 0, - │ "eve.tool.call_id": "", - │ "eve.tool.name": "search", - │ "eve.turn.id": "", - │ "eve.turn.sequence": 0, │ "scenario": "eve-instrumentation", │ "testRunId": "" │ } + │ └── eve.turn [task] + │ input: [ + │ { + │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ } + │ ] + │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ metadata: { + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ metrics: { + │ "completion_tokens": 48, + │ "prompt_cached_tokens": 12032, + │ "prompt_tokens": 13178, + │ "tokens": 13226 + │ } + │ ├── eve.step [llm] + │ │ input: [ + │ │ { + │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ │ "role": "user" + │ │ } + │ │ ] + │ │ output: [ + │ │ { + │ │ "finish_reason": "tool_calls", + │ │ "index": 0, + │ │ "message": { + │ │ "content": null, + │ │ "role": "assistant", + │ │ "tool_calls": [ + │ │ { + │ │ "function": { + │ │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ │ "name": "search" + │ │ }, + │ │ "id": "", + │ │ "type": "function" + │ │ } + │ │ ] + │ │ } + │ │ } + │ │ ] + │ │ metadata: { + │ │ "model": "gpt-5.4-mini", + │ │ "provider": "openai", + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ metrics: { + │ │ "completion_tokens": 26, + │ │ "prompt_cached_tokens": 5504, + │ │ "prompt_tokens": 6554, + │ │ "tokens": 6580 + │ │ } + │ ├── search [tool] + │ │ input: { + │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario" + │ │ } + │ │ output: { + │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario", + │ │ "title": "Eve instrumentation", + │ │ "url": "https://eve.dev/docs/guides/instrumentation" + │ │ } + │ │ metadata: { + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ └── eve.step [llm] + │ input: [ + │ { + │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ "name": "search" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ }, + │ { + │ "content": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + │ "name": "search", + │ "role": "tool", + │ "tool_call_id": "call_bsPk0zM9ENF7o5XXNuEmMjhA" + │ } + │ ] + │ output: [ + │ { + │ "finish_reason": "stop", + │ "index": 0, + │ "message": { + │ "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + │ "role": "assistant" + │ } + │ } + │ ] + │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ metrics: { + │ "completion_tokens": 22, + │ "prompt_cached_tokens": 6528, + │ "prompt_tokens": 6624, + │ "tokens": 6646 + │ } ├── eve.step [llm] │ input: [ │ { @@ -102,50 +196,36 @@ span_tree: │ "tool_calls": [ │ { │ "function": { - │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - │ "name": "search" + │ "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ "name": "researcher" │ }, │ "id": "", │ "type": "function" │ } │ ] - │ }, - │ { - │ "content": { - │ "query": "Run the Braintrust Eve instrumentation e2e scenario", - │ "title": "Eve instrumentation", - │ "url": "https://eve.dev/docs/guides/instrumentation" - │ }, - │ "name": "search", - │ "role": "tool", - │ "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" │ } │ ] - │ output: { - │ "message": { - │ "content": null, - │ "role": "assistant", - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - │ "name": "read" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] + │ output: [ + │ { + │ "finish_reason": "tool_calls", + │ "index": 0, + │ "message": { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + │ "name": "read" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ } │ } - │ } + │ ] │ metadata: { - │ "eve.agent.name": "eve-instrumentation-scenario", - │ "eve.channel.kind": "http", - │ "eve.finish_reason": "tool-calls", - │ "eve.model.id": "openai/gpt-5.4-mini", - │ "eve.session.id": "", - │ "eve.step.index": 1, - │ "eve.turn.id": "", - │ "eve.turn.sequence": 0, │ "model": "gpt-5.4-mini", │ "provider": "openai", │ "scenario": "eve-instrumentation", @@ -153,30 +233,21 @@ span_tree: │ } │ metrics: { │ "completion_tokens": 27, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 6634, - │ "tokens": 6661 + │ "prompt_cached_tokens": 6528, + │ "prompt_tokens": 6724, + │ "tokens": 6751 │ } ├── read [tool] │ input: { │ "url": "https://eve.dev/docs/guides/instrumentation" │ } │ output: { - │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", │ "section": "Runtime context", │ "title": "Eve instrumentation", │ "url": "https://eve.dev/docs/guides/instrumentation" │ } │ metadata: { - │ "eve.action.status": "completed", - │ "eve.agent.name": "eve-instrumentation-scenario", - │ "eve.channel.kind": "http", - │ "eve.session.id": "", - │ "eve.step.index": 1, - │ "eve.tool.call_id": "", - │ "eve.tool.name": "read", - │ "eve.turn.id": "", - │ "eve.turn.sequence": 0, │ "scenario": "eve-instrumentation", │ "testRunId": "" │ } @@ -192,24 +263,14 @@ span_tree: "tool_calls": [ { "function": { - "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "search" + "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "researcher" }, "id": "", "type": "function" } ] }, - { - "content": { - "query": "Run the Braintrust Eve instrumentation e2e scenario", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "name": "search", - "role": "tool", - "tool_call_id": "call_OJGkAZ5ouXaH7NjuUH2mSbk6" - }, { "content": null, "role": "assistant", @@ -225,41 +286,31 @@ span_tree: ] }, { - "content": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", - "section": "Runtime context", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - }, + "content": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", "name": "read", "role": "tool", - "tool_call_id": "call_kZ7AzZ9mCqataCzrSSLxRkFh" + "tool_call_id": "call_yRc6f1bJPthZhaIEzCySgUi0" } ] - output: { - "finish_reason": "stop", - "message": { - "content": "Final answer from read: Eve instrumentation — https://eve.dev/docs/guides/instrumentation — Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", - "role": "assistant" + output: [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "role": "assistant" + } } - } + ] metadata: { - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.finish_reason": "stop", - "eve.model.id": "openai/gpt-5.4-mini", - "eve.session.id": "", - "eve.step.index": 2, - "eve.turn.id": "", - "eve.turn.sequence": 0, "model": "gpt-5.4-mini", "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" } metrics: { - "completion_tokens": 40, - "prompt_cached_tokens": 0, - "prompt_tokens": 6716, - "tokens": 6756 + "completion_tokens": 66, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6806, + "tokens": 6872 } diff --git a/e2e/scenarios/eve-instrumentation/agent/instructions.md b/e2e/scenarios/eve-instrumentation/agent/instructions.md index a4715d143..42aaa617f 100644 --- a/e2e/scenarios/eve-instrumentation/agent/instructions.md +++ b/e2e/scenarios/eve-instrumentation/agent/instructions.md @@ -2,9 +2,10 @@ You are a deterministic fixture agent for Braintrust Eve instrumentation tests. For every user task: -1. Call the search tool exactly once with the full user message as the query. -2. After the search result is available, call the read tool exactly once with the - URL returned by search. +1. Call the researcher subagent exactly once with the full user message as the + message. +2. After the researcher result is available, call the read tool exactly once with + the URL https://eve.dev/docs/guides/instrumentation. 3. After the read result is available, answer with a single sentence that starts - with "Final answer from read:" and includes the search title, URL, and read - excerpt. + with "Final answer from read:" and includes the researcher result, read title, + URL, and read excerpt. diff --git a/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/agent.ts b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/agent.ts new file mode 100644 index 000000000..803ffb8e7 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/agent.ts @@ -0,0 +1,15 @@ +import { createOpenAI } from "@ai-sdk/openai"; +import { defineAgent } from "eve"; + +const openai = createOpenAI({ + ...(process.env.OPENAI_BASE_URL + ? { baseURL: process.env.OPENAI_BASE_URL } + : {}), +}); + +export default defineAgent({ + description: + "Research the Eve instrumentation documentation before the parent reads it.", + model: openai("gpt-5.4-mini"), + modelContextWindowTokens: 8_192, +}); diff --git a/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts new file mode 100644 index 000000000..2597dc318 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts @@ -0,0 +1,13 @@ +import { braintrustEveHook } from "braintrust"; +import { defineHook } from "eve/hooks"; + +export default defineHook( + braintrustEveHook({ + metadata: { + scenario: "eve-instrumentation", + ...(process.env.BRAINTRUST_E2E_RUN_ID + ? { testRunId: process.env.BRAINTRUST_E2E_RUN_ID } + : {}), + }, + }), +); diff --git a/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/instructions.md b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/instructions.md new file mode 100644 index 000000000..3fc818783 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/instructions.md @@ -0,0 +1,7 @@ +You are the deterministic researcher subagent for Braintrust Eve instrumentation tests. + +For every task: + +1. Call the search tool exactly once with the full task as the query. +2. After the search result is available, answer with a single sentence that starts + with "Researcher result:" and includes the search title and URL. diff --git a/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/tools/search.ts b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/tools/search.ts new file mode 100644 index 000000000..dc95b8ea1 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/tools/search.ts @@ -0,0 +1,16 @@ +import { defineTool } from "eve/tools"; +import { z } from "zod"; + +export default defineTool({ + description: "Searches the Eve instrumentation documentation fixture.", + inputSchema: z.object({ + query: z.string(), + }), + async execute({ query }) { + return { + query, + title: "Eve instrumentation", + url: "https://eve.dev/docs/guides/instrumentation", + }; + }, +}); diff --git a/e2e/scenarios/eve-instrumentation/agent/tools/read.ts b/e2e/scenarios/eve-instrumentation/agent/tools/read.ts index 66e0e261b..679961e05 100644 --- a/e2e/scenarios/eve-instrumentation/agent/tools/read.ts +++ b/e2e/scenarios/eve-instrumentation/agent/tools/read.ts @@ -9,7 +9,7 @@ export default defineTool({ async execute({ url }) { return { excerpt: - "Eve hooks expose runtime stream events that Braintrust maps into a flat turn trace.", + "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", section: "Runtime context", title: "Eve instrumentation", url, diff --git a/e2e/scenarios/eve-instrumentation/scenario.test.ts b/e2e/scenarios/eve-instrumentation/scenario.test.ts index 677ed4838..8ac331235 100644 --- a/e2e/scenarios/eve-instrumentation/scenario.test.ts +++ b/e2e/scenarios/eve-instrumentation/scenario.test.ts @@ -46,24 +46,28 @@ describe("eve instrumentation", () => { ); }, TIMEOUT_MS); - test("captures a flat Eve turn trace", async () => { + test("captures a nested Eve local subagent trace", async () => { const root = findLatestSpan(events, "eve.turn"); - const steps = findChildSpans(events, "eve.step", root?.span.id).sort( - (left, right) => - Number(left.metadata?.["eve.step.index"]) - - Number(right.metadata?.["eve.step.index"]), + const steps = findChildSpans(events, "eve.step", root?.span.id); + const researcher = findChildSpans(events, "researcher", root?.span.id)[0]; + const childTurn = findChildSpans( + events, + "eve.turn", + researcher?.span.id, + )[0]; + const childSearch = findLatestChildSpan( + events, + "search", + childTurn?.span.id, ); - const search = findLatestChildSpan(events, "search", root?.span.id); const read = findLatestChildSpan(events, "read", root?.span.id); expect(root).toBeDefined(); expect(root?.span.type).toBe("task"); + expect(root?.span.parentIds).toEqual([]); expect(root?.metadata).toMatchObject({ - "eve.agent.name": "eve-instrumentation-scenario", - "eve.channel.kind": "http", - "eve.model.id": expect.any(String), - model: "gpt-5.4-mini", - provider: "openai", + scenario: "eve-instrumentation", + testRunId: expect.any(String), }); expect(root?.metrics?.completion_tokens).toEqual(expect.any(Number)); expect(root?.metrics?.prompt_tokens).toEqual(expect.any(Number)); @@ -76,19 +80,40 @@ describe("eve instrumentation", () => { expect(step.span.parentIds).toEqual([root?.span.id]); expect(step.input).toEqual(expect.arrayContaining([expect.anything()])); expect(step.metadata).toMatchObject({ - "eve.model.id": expect.any(String), model: "gpt-5.4-mini", provider: "openai", + scenario: "eve-instrumentation", + testRunId: expect.any(String), }); } - expect(search).toBeDefined(); - expect(search?.span.type).toBe("tool"); - expect(search?.span.parentIds).toEqual([root?.span.id]); - expect(search?.input).toMatchObject({ + expect(researcher).toBeDefined(); + expect(researcher?.span.type).toBe("tool"); + expect(researcher?.span.parentIds).toEqual([root?.span.id]); + expect(researcher?.input).toMatchObject({ + message: expect.stringContaining("Braintrust Eve instrumentation"), + }); + expect(researcher?.metadata).toMatchObject({ + scenario: "eve-instrumentation", + testRunId: expect.any(String), + }); + expect(researcher?.output).toContain("Researcher result"); + + expect(childTurn).toBeDefined(); + expect(childTurn?.span.parentIds).toEqual([researcher?.span.id]); + expect(childTurn?.span.rootId).toEqual(root?.span.rootId); + expect(childTurn?.metadata).toMatchObject({ + scenario: "eve-instrumentation", + testRunId: expect.any(String), + }); + + expect(childSearch).toBeDefined(); + expect(childSearch?.span.type).toBe("tool"); + expect(childSearch?.span.parentIds).toEqual([childTurn?.span.id]); + expect(childSearch?.input).toMatchObject({ query: expect.stringContaining("Braintrust Eve instrumentation"), }); - expect(search?.output).toMatchObject({ + expect(childSearch?.output).toMatchObject({ title: "Eve instrumentation", }); @@ -103,14 +128,6 @@ describe("eve instrumentation", () => { title: "Eve instrumentation", }); - await matchSpanTreeSnapshot(events, spanTreeSnapshotPath, { - normalize: { - additionalProviderIdKeys: [ - "eve.session.id", - "eve.tool.call_id", - "eve.turn.id", - ], - }, - }); + await matchSpanTreeSnapshot(events, spanTreeSnapshotPath); }); }); diff --git a/e2e/scenarios/eve-instrumentation/scenario.ts b/e2e/scenarios/eve-instrumentation/scenario.ts index 4d1027b1e..84a4bf0a8 100644 --- a/e2e/scenarios/eve-instrumentation/scenario.ts +++ b/e2e/scenarios/eve-instrumentation/scenario.ts @@ -48,7 +48,11 @@ async function main() { throw new Error(`Eve session create did not return a sessionId`); } - await streamUntilTurnCompleted(baseUrl, body.sessionId); + await streamUntilTurnCompleted( + baseUrl, + body.sessionId, + new Set([body.sessionId]), + ); await new Promise((resolve) => setTimeout(resolve, 5000)); } finally { await stopServer(server); @@ -135,8 +139,10 @@ async function waitForEve( async function streamUntilTurnCompleted( baseUrl: string, sessionId: string, + seenSessionIds: Set, ): Promise { const controller = new AbortController(); + const childStreams: Promise[] = []; const response = await fetch( `${baseUrl}/eve/v1/session/${sessionId}/stream`, { @@ -167,7 +173,7 @@ async function streamUntilTurnCompleted( continue; } const event = JSON.parse(trimmed) as { - data?: { message?: string }; + data?: { childSessionId?: string; message?: string }; type?: string; }; if ( @@ -179,7 +185,22 @@ async function streamUntilTurnCompleted( `Eve emitted ${event.type}: ${event.data?.message ?? trimmed}`, ); } + if ( + event.type === "subagent.called" && + typeof event.data?.childSessionId === "string" && + !seenSessionIds.has(event.data.childSessionId) + ) { + seenSessionIds.add(event.data.childSessionId); + childStreams.push( + streamUntilTurnCompleted( + baseUrl, + event.data.childSessionId, + seenSessionIds, + ), + ); + } if (event.type === "turn.completed") { + await Promise.all(childStreams); return; } } diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index 6e7bf26b1..fb4bdab43 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -1,12 +1,26 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { createHash } from "node:crypto"; import { configureNode } from "../../node/config"; -import { _exportsForTestingOnly, initLogger } from "../../logger"; +import { + _exportsForTestingOnly, + initLogger, + startSpan, + withCurrent, +} from "../../logger"; import { braintrustEveHook } from "./eve-plugin"; import type { EveHandleMessageStreamEvent, EveHookContext, } from "../../vendor-sdk-types/eve"; +function deterministicEveIdForTest(...parts: string[]): string { + return createHash("sha256") + .update(parts.map((part) => `${part.length}:${part}`).join("\0")) + .digest("hex") + .slice(0, 32) + .replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, "$1-$2-$3-$4-$5"); +} + try { configureNode(); } catch { @@ -59,12 +73,11 @@ describe("braintrustEveHook", () => { }; const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); const expectedModelMetadata = { - "eve.model.id": "eve-mock/braintrust-eve-mock", model: "braintrust-eve-mock", provider: "eve-mock", }; - emit({ + await emit({ data: { runtime: { agentId: "agent-id", @@ -76,12 +89,12 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.000Z" }, type: "session.started", }); - emit({ + await emit({ data: { sequence: 0, turnId: "turn-flat-tree" }, meta: { at: "2026-01-01T00:00:00.010Z" }, type: "turn.started", }); - emit({ + await emit({ data: { message: "Search then read", sequence: 0, @@ -90,12 +103,12 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.020Z" }, type: "message.received", }); - emit({ + await emit({ data: { sequence: 0, stepIndex: 0, turnId: "turn-flat-tree" }, meta: { at: "2026-01-01T00:00:00.030Z" }, type: "step.started", }); - emit({ + await emit({ data: { actions: [ { @@ -112,7 +125,7 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.040Z" }, type: "actions.requested", }); - emit({ + await emit({ data: { error: undefined, result: { @@ -129,7 +142,7 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.050Z" }, type: "action.result", }); - emit({ + await emit({ data: { finishReason: "tool-calls", sequence: 0, @@ -146,12 +159,12 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.060Z" }, type: "step.completed", }); - emit({ + await emit({ data: { sequence: 0, stepIndex: 1, turnId: "turn-flat-tree" }, meta: { at: "2026-01-01T00:00:00.070Z" }, type: "step.started", }); - emit({ + await emit({ data: { finishReason: "stop", message: "Here is the Eve instrumentation guide.", @@ -162,7 +175,7 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.080Z" }, type: "message.completed", }); - emit({ + await emit({ data: { finishReason: "stop", sequence: 0, @@ -176,7 +189,7 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.090Z" }, type: "step.completed", }); - emit({ + await emit({ data: { sequence: 0, turnId: "turn-flat-tree" }, meta: { at: "2026-01-01T00:00:00.100Z" }, type: "turn.completed", @@ -188,15 +201,9 @@ describe("braintrustEveHook", () => { const root = spans.find( (span) => span.span_attributes?.name === "eve.turn", ); - const steps = spans - .filter((span) => - String(span.span_attributes?.name).startsWith("eve.step"), - ) - .sort( - (left, right) => - Number(left.metadata?.["eve.step.index"]) - - Number(right.metadata?.["eve.step.index"]), - ); + const steps = spans.filter((span) => + String(span.span_attributes?.name).startsWith("eve.step"), + ); const tool = spans.find((span) => span.span_attributes?.name === "search"); expect(spans.map((span) => span.span_attributes?.name)).toEqual([ @@ -208,11 +215,6 @@ describe("braintrustEveHook", () => { expect(root).toMatchObject({ input: [{ content: "Search then read", role: "user" }], metadata: { - ...expectedModelMetadata, - "eve.agent.name": "eve-test-agent", - "eve.channel.kind": "http", - "eve.session.id": "session-flat-tree", - "eve.turn.id": "turn-flat-tree", scenario: "eve-plugin-unit", testRunId: "test-run-flat-tree", }, @@ -243,10 +245,9 @@ describe("braintrustEveHook", () => { [root?.span_id], [root?.span_id], ]); - for (const [index, step] of steps.entries()) { - expect(step.metadata).toMatchObject({ + for (const step of steps) { + expect(step.metadata).toEqual({ ...expectedModelMetadata, - "eve.step.index": index, scenario: "eve-plugin-unit", testRunId: "test-run-flat-tree", }); @@ -271,7 +272,7 @@ describe("braintrustEveHook", () => { ], }, { - content: { hits: ["eve.dev/docs"] }, + content: JSON.stringify({ hits: ["eve.dev/docs"] }), name: "search", role: "tool", tool_call_id: "call-search", @@ -280,9 +281,8 @@ describe("braintrustEveHook", () => { expect(tool).toMatchObject({ input: { query: "Eve instrumentation" }, metadata: { - "eve.step.index": 0, - "eve.tool.call_id": "call-search", - "eve.tool.name": "search", + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", }, output: { hits: ["eve.dev/docs"] }, span_attributes: { @@ -291,30 +291,34 @@ describe("braintrustEveHook", () => { }, span_parents: [root?.span_id], }); - expect(tool?.metadata).not.toHaveProperty("eve.model.id"); expect(tool?.metadata).not.toHaveProperty("model"); expect(tool?.metadata).not.toHaveProperty("provider"); - expect(steps[0]?.output).toMatchObject({ - message: { - tool_calls: [ - { - function: { - arguments: JSON.stringify({ query: "Eve instrumentation" }), - name: "search", + expect(steps[0]?.output).toMatchObject([ + { + finish_reason: "tool_calls", + message: { + tool_calls: [ + { + function: { + arguments: JSON.stringify({ query: "Eve instrumentation" }), + name: "search", + }, + id: "call-search", + type: "function", }, - id: "call-search", - type: "function", - }, - ], + ], + }, }, - }); - expect(steps[1]?.output).toMatchObject({ - finish_reason: "stop", - message: { - content: "Here is the Eve instrumentation guide.", - role: "assistant", + ]); + expect(steps[1]?.output).toMatchObject([ + { + finish_reason: "stop", + message: { + content: "Here is the Eve instrumentation guide.", + role: "assistant", + }, }, - }); + ]); }); it("merges incremental tool call requests into one assistant message", async () => { @@ -328,11 +332,11 @@ describe("braintrustEveHook", () => { }; const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); - emit({ + await emit({ data: { sequence: 0, turnId: "turn-incremental-tools" }, type: "turn.started", }); - emit({ + await emit({ data: { message: "Search then read", sequence: 0, @@ -340,11 +344,11 @@ describe("braintrustEveHook", () => { }, type: "message.received", }); - emit({ + await emit({ data: { sequence: 0, stepIndex: 0, turnId: "turn-incremental-tools" }, type: "step.started", }); - emit({ + await emit({ data: { actions: [ { @@ -360,7 +364,7 @@ describe("braintrustEveHook", () => { }, type: "actions.requested", }); - emit({ + await emit({ data: { actions: [ { @@ -376,7 +380,7 @@ describe("braintrustEveHook", () => { }, type: "actions.requested", }); - emit({ + await emit({ data: { result: { callId: "call-search", @@ -391,7 +395,7 @@ describe("braintrustEveHook", () => { }, type: "action.result", }); - emit({ + await emit({ data: { result: { callId: "call-read", @@ -406,7 +410,7 @@ describe("braintrustEveHook", () => { }, type: "action.result", }); - emit({ + await emit({ data: { finishReason: "tool-calls", sequence: 0, @@ -415,11 +419,11 @@ describe("braintrustEveHook", () => { }, type: "step.completed", }); - emit({ + await emit({ data: { sequence: 0, stepIndex: 1, turnId: "turn-incremental-tools" }, type: "step.started", }); - emit({ + await emit({ data: { finishReason: "stop", message: "Done.", @@ -429,7 +433,7 @@ describe("braintrustEveHook", () => { }, type: "message.completed", }); - emit({ + await emit({ data: { finishReason: "stop", sequence: 0, @@ -438,7 +442,7 @@ describe("braintrustEveHook", () => { }, type: "step.completed", }); - emit({ + await emit({ data: { sequence: 0, turnId: "turn-incremental-tools" }, type: "turn.completed", }); @@ -446,24 +450,21 @@ describe("braintrustEveHook", () => { const spans = (await backgroundLogger.drain()) as Array< Record >; - const steps = spans - .filter((span) => - String(span.span_attributes?.name).startsWith("eve.step"), - ) - .sort( - (left, right) => - Number(left.metadata?.["eve.step.index"]) - - Number(right.metadata?.["eve.step.index"]), - ); + const steps = spans.filter((span) => + String(span.span_attributes?.name).startsWith("eve.step"), + ); - expect(steps[0]?.output).toMatchObject({ - message: { - tool_calls: [ - { id: "call-search", function: { name: "search" } }, - { id: "call-read", function: { name: "read" } }, - ], + expect(steps[0]?.output).toMatchObject([ + { + finish_reason: "tool_calls", + message: { + tool_calls: [ + { id: "call-search", function: { name: "search" } }, + { id: "call-read", function: { name: "read" } }, + ], + }, }, - }); + ]); const assistantToolMessages = steps[1]?.input.filter( (message: Record) => message.role === "assistant" && Array.isArray(message.tool_calls), @@ -479,12 +480,441 @@ describe("braintrustEveHook", () => { }); }); + it("uses deterministic ids and nests local subagent sessions from Eve lineage", async () => { + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + const parentCtx: EveHookContext = { + agent: { name: "eve-parent-agent" }, + channel: { kind: "http" }, + session: { id: "session-parent" }, + }; + const childCtx: EveHookContext = { + agent: { name: "eve-child-agent", nodeId: "researcher-node" }, + channel: { kind: "subagent" }, + session: { + id: "session-child", + parent: { + callId: "call-researcher", + rootSessionId: "session-parent", + sessionId: "session-parent", + turn: { id: "turn-parent", sequence: 0 }, + }, + }, + }; + const emitParent = (event: EveHandleMessageStreamEvent) => + wildcard?.(event, parentCtx); + const emitChild = (event: EveHandleMessageStreamEvent) => + wildcard?.(event, childCtx); + + await emitParent({ + data: { sequence: 0, turnId: "turn-parent" }, + type: "turn.started", + }); + await emitParent({ + data: { + message: "Research Eve tracing", + sequence: 0, + turnId: "turn-parent", + }, + type: "message.received", + }); + await emitParent({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-parent" }, + type: "step.started", + }); + await emitParent({ + data: { + actions: [ + { + callId: "call-researcher", + input: { message: "Find the relevant section" }, + kind: "subagent-call", + name: "researcher", + subagentName: "researcher", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-parent", + }, + type: "actions.requested", + }); + await emitParent({ + data: { + callId: "call-researcher", + childSessionId: "session-child", + name: "researcher", + sequence: 0, + toolName: "researcher", + turnId: "turn-parent", + }, + type: "subagent.called", + }); + + await emitChild({ + data: { sequence: 0, turnId: "turn-child" }, + type: "turn.started", + }); + await emitChild({ + data: { + message: "Find the relevant section", + sequence: 0, + turnId: "turn-child", + }, + type: "message.received", + }); + await emitChild({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-child" }, + type: "step.started", + }); + await emitChild({ + data: { + actions: [ + { + callId: "call-search", + input: { query: "nested eve" }, + kind: "tool-call", + toolName: "search", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-child", + }, + type: "actions.requested", + }); + await emitChild({ + data: { + result: { + callId: "call-search", + kind: "tool-result", + output: { title: "Nested Eve" }, + toolName: "search", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-child", + }, + type: "action.result", + }); + await emitChild({ + data: { + finishReason: "stop", + message: "Child found Nested Eve.", + sequence: 0, + stepIndex: 0, + turnId: "turn-child", + }, + type: "message.completed", + }); + await emitChild({ + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 0, + turnId: "turn-child", + }, + type: "step.completed", + }); + await emitChild({ + data: { sequence: 0, turnId: "turn-child" }, + type: "turn.completed", + }); + + await emitParent({ + data: { + callId: "call-researcher", + output: "Child found Nested Eve.", + sequence: 0, + status: "completed", + subagentName: "researcher", + turnId: "turn-parent", + }, + type: "subagent.completed", + }); + await emitParent({ + data: { + result: { + callId: "call-researcher", + kind: "subagent-result", + output: "Child found Nested Eve.", + subagentName: "researcher", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-parent", + }, + type: "action.result", + }); + await emitParent({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-parent" }, + type: "step.started", + }); + await emitParent({ + data: { + actions: [ + { + callId: "call-read", + input: { url: "https://eve.dev/docs/guides/instrumentation" }, + kind: "tool-call", + toolName: "read", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-parent", + }, + type: "actions.requested", + }); + await emitParent({ + data: { + result: { + callId: "call-read", + kind: "tool-result", + output: { title: "Runtime context" }, + toolName: "read", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-parent", + }, + type: "action.result", + }); + await emitParent({ + data: { + finishReason: "tool-calls", + sequence: 0, + stepIndex: 0, + turnId: "turn-parent", + }, + type: "step.completed", + }); + await emitParent({ + data: { sequence: 0, stepIndex: 1, turnId: "turn-parent" }, + type: "step.started", + }); + await emitParent({ + data: { + finishReason: "stop", + message: "Parent used the child result.", + sequence: 0, + stepIndex: 1, + turnId: "turn-parent", + }, + type: "message.completed", + }); + await emitParent({ + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 1, + turnId: "turn-parent", + }, + type: "step.completed", + }); + await emitParent({ + data: { sequence: 0, turnId: "turn-parent" }, + type: "turn.completed", + }); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const parentTurnId = deterministicEveIdForTest( + "eve:turn", + "session-parent", + "turn-parent", + ); + const childTurnId = deterministicEveIdForTest( + "eve:turn", + "session-child", + "turn-child", + ); + const subagentSpanId = deterministicEveIdForTest( + "eve:subagent", + "session-parent", + "call-researcher", + ); + const parentTurn = spans.find( + (span) => + span.span_attributes?.name === "eve.turn" && + span.span_id === parentTurnId, + ); + const subagentSpans = spans.filter( + (span) => span.span_attributes?.name === "researcher", + ); + const childTurn = spans.find( + (span) => + span.span_attributes?.name === "eve.turn" && + span.span_id === childTurnId, + ); + const childSearch = spans.find( + (span) => + span.span_attributes?.name === "search" && + span.span_parents?.[0] === childTurnId, + ); + const parentRead = spans.find( + (span) => + span.span_attributes?.name === "read" && + span.span_parents?.[0] === parentTurnId, + ); + const parentSteps = spans.filter( + (span) => + span.span_attributes?.name === "eve.step" && + span.span_parents?.[0] === parentTurnId, + ); + + expect(parentTurn).toBeDefined(); + expect(subagentSpans).toHaveLength(1); + expect(subagentSpans[0]?.span_id).toBe(subagentSpanId); + expect(childTurn).toBeDefined(); + expect(childSearch).toBeDefined(); + expect(parentRead).toBeDefined(); + expect(parentSteps).toHaveLength(3); + expect(parentTurn?.span_parents ?? []).toEqual([]); + expect(parentTurn?.span_id).toBe(parentTurnId); + expect(parentTurn?.span_id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, + ); + expect(parentTurn?.root_span_id).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, + ); + expect(subagentSpans[0]?.span_parents).toEqual([parentTurn?.span_id]); + expect(childTurn?.span_parents).toEqual([subagentSpans[0]?.span_id]); + expect(childTurn?.root_span_id).toBe(parentTurn?.root_span_id); + expect(childTurn?.metadata ?? {}).toEqual({}); + expect(subagentSpans[0]?.metadata ?? {}).toEqual({}); + expect(childSearch?.span_parents).toEqual([childTurn?.span_id]); + expect(childSearch?.span_id).toBe( + deterministicEveIdForTest( + "eve:tool", + "session-child", + "turn-child", + "call-search", + ), + ); + expect(parentRead?.span_id).toBe( + deterministicEveIdForTest( + "eve:tool", + "session-parent", + "turn-parent", + "call-read", + ), + ); + expect(parentSteps.map((span) => span.span_id)).toEqual([ + deterministicEveIdForTest( + "eve:step", + "session-parent", + "turn-parent", + "0", + ), + deterministicEveIdForTest( + "eve:step", + "session-parent", + "turn-parent", + "1", + ), + deterministicEveIdForTest( + "eve:step", + "session-parent", + "turn-parent", + "2", + ), + ]); + expect(spans.map((span) => span.span_attributes?.name)).toEqual([ + "eve.turn", + "eve.step", + "researcher", + "eve.turn", + "eve.step", + "search", + "eve.step", + "read", + "eve.step", + ]); + + Reflect.deleteProperty(globalThis, Symbol.for("braintrust.eve.bridge")); + backgroundLogger = _exportsForTestingOnly.useTestBackgroundLogger(); + initLogger({ + projectName: "eve-plugin.test.ts", + projectId: "test-project-id", + }); + const replay = braintrustEveHook().events?.["*"]; + await replay?.( + { + data: { sequence: 0, turnId: "turn-parent" }, + type: "turn.started", + }, + parentCtx, + ); + await replay?.( + { + data: { sequence: 0, turnId: "turn-parent" }, + type: "turn.completed", + }, + parentCtx, + ); + const replaySpans = (await backgroundLogger.drain()) as Array< + Record + >; + expect( + replaySpans.find((span) => span.span_attributes?.name === "eve.turn") + ?.span_id, + ).toBe(parentTurn?.span_id); + }); + + it("parents root Eve turns under the active Braintrust span", async () => { + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { + session: { id: "session-wrapped" }, + }; + const parent = startSpan({ name: "workflow" }); + await withCurrent(parent, async () => { + await wildcard?.( + { + data: { sequence: 0, turnId: "turn-wrapped" }, + type: "turn.started", + }, + ctx, + ); + await wildcard?.( + { + data: { sequence: 0, turnId: "turn-wrapped" }, + type: "turn.completed", + }, + ctx, + ); + }); + parent.end(); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const turn = spans.find( + (span) => span.span_attributes?.name === "eve.turn", + ); + + expect(turn?.span_id).toBe( + deterministicEveIdForTest("eve:turn", "session-wrapped", "turn-wrapped"), + ); + expect(turn?.span_parents).toEqual([parent.spanId]); + expect(turn?.root_span_id).toBe(parent.rootSpanId); + }); + it("does not throw when Eve emits malformed events or failures", async () => { const wildcard = braintrustEveHook().events?.["*"]; expect(wildcard).toBeDefined(); - expect(() => wildcard?.({ bad: true } as never, {})).not.toThrow(); - expect(() => + await expect( + wildcard?.({ bad: true } as never, {}), + ).resolves.toBeUndefined(); + await expect( wildcard?.( { data: { @@ -498,8 +928,8 @@ describe("braintrustEveHook", () => { }, {}, ), - ).not.toThrow(); - expect(() => + ).resolves.toBeUndefined(); + await expect( wildcard?.( { data: { runtime: { modelId: 123 } }, @@ -507,7 +937,7 @@ describe("braintrustEveHook", () => { } as never, { session: { id: "session-malformed-runtime" } }, ), - ).not.toThrow(); + ).resolves.toBeUndefined(); const spans = await backgroundLogger.drain(); expect(spans).toEqual([]); diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index 5b7b0bd4e..5eb045bd2 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -1,12 +1,15 @@ import { toLoggedError } from "../core"; import { debugLogger } from "../../debug-logger"; -import { flush, logError, startSpan, withCurrent } from "../../logger"; -import type { Span, StartSpanArgs } from "../../logger"; +import { flush, logError, startSpan } from "../../logger"; +import type { Span } from "../../logger"; import { SpanTypeAttribute, isObject } from "../../../util/index"; import type { + EveAssistantStepFinishReason, EveHandleMessageStreamEvent, EveHookContext, EveHookDefinition, + EveRuntimeActionRequest, + EveRuntimeActionResult, EveRuntimeToolCallActionRequest, EveRuntimeToolResultActionResult, } from "../../vendor-sdk-types/eve"; @@ -34,23 +37,36 @@ type AssistantToolCallMessage = { type StepState = SpanState & { assistantToolCallMessage?: AssistantToolCallMessage; metrics: Record; + ordinal: number; output?: unknown; toolCalls: ToolCall[]; }; type TurnState = SpanState & { inputLogged: boolean; + key: string; messages: unknown[]; metrics: Record; + nextStepOrdinal: number; output?: unknown; stepsByIndex: Map; }; type ToolState = SpanState & { + kind: "subagent" | "tool"; stepIndex?: number; + stepOrdinal?: number; turnKey: string; }; +type ParentLineage = { + callId: string; + rootSessionId: string; + sessionId: string; + turnId?: string; + turnSequence?: number; +}; + const EVE_BRIDGE = Symbol.for("braintrust.eve.bridge"); /** Manual hook instrumentation for eve runtime stream events. */ @@ -59,8 +75,8 @@ export function braintrustEveHook( ): EveHookDefinition { return { events: { - "*": (event: EveHandleMessageStreamEvent, ctx: EveHookContext) => { - getEveBridge().handle(event, ctx, options.metadata); + "*": async (event: EveHandleMessageStreamEvent, ctx: EveHookContext) => { + await getEveBridge().handle(event, ctx, options.metadata); }, }, }; @@ -81,6 +97,8 @@ class EveBridge { string, { metadata: Record } >(); + private completedToolKeys = new Set(); + private queue = Promise.resolve(); private toolsByCallKey = new Map(); private turnsByKey = new Map(); @@ -88,35 +106,41 @@ class EveBridge { event: unknown, ctx: unknown, hookMetadata?: Record, - ): void { + ): Promise { if (!isObject(event)) { - return; + return Promise.resolve(); } - try { - this.handleEvent(event as EveHandleMessageStreamEvent, ctx, hookMetadata); - } catch (error) { - logInstrumentationError("Eve hook event", error); - } + const next = this.queue + .then(() => + this.handleEvent( + event as EveHandleMessageStreamEvent, + ctx, + hookMetadata, + ), + ) + .catch(() => undefined); + this.queue = next; + return next; } - private handleEvent( + private async handleEvent( event: EveHandleMessageStreamEvent, ctx: unknown, hookMetadata?: Record, - ): void { + ): Promise { switch (event.type) { case "session.started": this.handleSessionStarted(event, ctx); return; case "turn.started": - this.handleTurnStarted(event, ctx, hookMetadata); + await this.handleTurnStarted(event, ctx, hookMetadata); return; case "message.received": - this.handleMessageReceived(event, ctx, hookMetadata); + await this.handleMessageReceived(event, ctx, hookMetadata); return; case "step.started": - this.handleStepStarted(event, ctx, hookMetadata); + await this.handleStepStarted(event, ctx, hookMetadata); return; case "message.completed": this.handleMessageCompleted(event, ctx); @@ -125,10 +149,16 @@ class EveBridge { this.handleResultCompleted(event, ctx); return; case "actions.requested": - this.handleActionsRequested(event, ctx, hookMetadata); + await this.handleActionsRequested(event, ctx, hookMetadata); return; case "action.result": - this.handleActionResult(event, ctx, hookMetadata); + await this.handleActionResult(event, ctx, hookMetadata); + return; + case "subagent.called": + await this.handleSubagentCalled(event, ctx, hookMetadata); + return; + case "subagent.completed": + await this.handleSubagentCompleted(event, ctx, hookMetadata); return; case "step.completed": this.handleStepCompleted(event, ctx); @@ -169,57 +199,52 @@ class EveBridge { continue; } - turn.metadata = { ...turn.metadata, ...metadata }; - safeLog(turn.span, { metadata: turn.metadata }); for (const step of turn.stepsByIndex.values()) { step.metadata = { ...step.metadata, ...metadata }; - safeLog(step.span, { metadata: step.metadata }); + step.span.log({ metadata: step.metadata }); } } } - private handleTurnStarted( + private async handleTurnStarted( event: Extract, ctx: unknown, hookMetadata?: Record, - ): void { + ): Promise { const sessionId = sessionIdFromContext(ctx); if (!sessionId) { return; } const key = turnKey(sessionId, event.data.turnId); - const metadata = this.turnMetadata(sessionId, event, ctx, hookMetadata); + const metadata = { ...(hookMetadata ?? {}) }; const existing = this.turnsByKey.get(key); if (existing) { existing.metadata = metadata; - safeLog(existing.span, { metadata }); + existing.span.log({ metadata }); return; } - const span = startSpan({ - event: { metadata }, - name: "eve.turn", - spanAttributes: { type: SpanTypeAttribute.TASK }, - startTime: eventTime(event), - }); - safeLog(span, { metadata }); + const span = await this.startTurnSpan(sessionId, event, ctx, metadata); + span.log({ metadata }); this.turnsByKey.set(key, { inputLogged: false, + key, messages: [], metadata, metrics: {}, + nextStepOrdinal: 0, span, stepsByIndex: new Map(), }); } - private handleMessageReceived( + private async handleMessageReceived( event: Extract, ctx: unknown, hookMetadata?: Record, - ): void { - const turn = this.ensureTurn(event, ctx, hookMetadata); + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); if (!turn) { return; } @@ -228,41 +253,59 @@ class EveBridge { turn.messages.push(...input); if (!turn.inputLogged) { turn.inputLogged = true; - safeLog(turn.span, { input }); + turn.span.log({ input }); } } - private handleStepStarted( + private async handleStepStarted( event: Extract, ctx: unknown, hookMetadata?: Record, - ): void { - const turn = this.ensureTurn(event, ctx, hookMetadata); - if (!turn) { + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { return; } + const existing = turn.stepsByIndex.get(event.data.stepIndex); + if (existing) { + existing.span.log({ + metadata: existing.metadata, + metrics: existing.metrics, + output: existing.output, + }); + const endTime = eventTime(event); + existing.span.end(endTime === undefined ? undefined : { endTime }); + } + + const stepOrdinal = turn.nextStepOrdinal++; const metadata = { ...turn.metadata, - "eve.step.index": event.data.stepIndex, + ...(this.sessionsById.get(sessionId)?.metadata ?? {}), }; - const span = startEveSpan(turn.span, { + const input = turn.messages.length > 0 ? [...turn.messages] : undefined; + const [eventId, spanId] = await Promise.all([ + rowIdForStep(sessionId, event.data.turnId, stepOrdinal), + spanIdForStep(sessionId, event.data.turnId, stepOrdinal), + ]); + const span = startChildSpan(turn.span, { event: { - input: turn.messages.length > 0 ? [...turn.messages] : undefined, + id: eventId, + input, metadata, }, name: "eve.step", spanAttributes: { type: SpanTypeAttribute.LLM }, + spanId, startTime: eventTime(event), }); - safeLog(span, { - input: turn.messages.length > 0 ? [...turn.messages] : undefined, - metadata, - }); + span.log({ input, metadata }); turn.stepsByIndex.set(event.data.stepIndex, { metadata, metrics: {}, + ordinal: stepOrdinal, span, toolCalls: [], }); @@ -277,16 +320,19 @@ class EveBridge { return; } - step.output = { - finish_reason: event.data.finishReason, - message: { - content: event.data.message, - role: "assistant", - ...(step.toolCalls.length > 0 - ? { tool_calls: [...step.toolCalls] } - : {}), + step.output = [ + { + finish_reason: normalizedFinishReason(event.data.finishReason), + index: 0, + message: { + content: event.data.message, + role: "assistant", + ...(step.toolCalls.length > 0 + ? { tool_calls: [...step.toolCalls] } + : {}), + }, }, - }; + ]; if (step.assistantToolCallMessage) { step.assistantToolCallMessage.content = event.data.message; step.assistantToolCallMessage.tool_calls = [...step.toolCalls]; @@ -308,7 +354,16 @@ class EveBridge { ): void { const step = this.stepForEvent(event, ctx); if (step) { - step.output = event.data.result; + step.output = [ + { + finish_reason: "stop", + index: 0, + message: { + content: event.data.result, + role: "assistant", + }, + }, + ]; } const turn = this.turnForEvent(event, ctx); @@ -317,76 +372,54 @@ class EveBridge { } } - private handleActionsRequested( + private async handleActionsRequested( event: Extract, ctx: unknown, hookMetadata?: Record, - ): void { - const turn = this.ensureTurn(event, ctx, hookMetadata); + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); const sessionId = sessionIdFromContext(ctx); if (!turn || !sessionId) { return; } const step = turn.stepsByIndex.get(event.data.stepIndex); - const toolActions = event.data.actions.filter(isToolCallAction); - if (toolActions.length === 0) { + const traceActions = event.data.actions.filter(isTraceableActionRequest); + if (traceActions.length === 0) { return; } - for (const action of toolActions) { + for (const action of traceActions) { if (step) { - const toolCall = openAIToolCallFromAction(action); + const toolCall = toolCallMessageFromAction(action); if (!step.toolCalls.some((existing) => existing.id === toolCall.id)) { step.toolCalls.push(toolCall); } } - const key = toolKey(sessionId, action.callId); - if (this.toolsByCallKey.has(key)) { - continue; + if (isToolCallAction(action)) { + await this.startRequestedTool(event, turn, sessionId, action, step); + } else if (isLocalSubagentCallAction(action)) { + await this.startRequestedSubagent(event, turn, sessionId, action, step); } - - const metadata = { - ...withoutModelMetadata(turn.metadata), - "eve.step.index": event.data.stepIndex, - "eve.tool.call_id": action.callId, - "eve.tool.name": action.toolName, - }; - const span = startEveSpan(turn.span, { - event: { - input: action.input, - metadata, - }, - name: action.toolName, - spanAttributes: { type: SpanTypeAttribute.TOOL }, - startTime: eventTime(event), - }); - safeLog(span, { - input: action.input, - metadata, - }); - this.toolsByCallKey.set(key, { - metadata, - span, - stepIndex: event.data.stepIndex, - turnKey: turnKey(sessionId, event.data.turnId), - }); } if (!step) { turn.messages.push({ content: null, role: "assistant", - tool_calls: toolActions.map(openAIToolCallFromAction), + tool_calls: traceActions.map(toolCallMessageFromAction), }); return; } - const outputMessage = - step.output && isObject(step.output) - ? Reflect.get(step.output, "message") + const outputChoice = + Array.isArray(step.output) && isObject(step.output[0]) + ? step.output[0] : undefined; + const outputMessage = outputChoice + ? Reflect.get(outputChoice, "message") + : undefined; const content = isObject(outputMessage) ? Reflect.get(outputMessage, "content") : null; @@ -405,68 +438,227 @@ class EveBridge { if (isObject(outputMessage)) { Reflect.set(outputMessage, "tool_calls", [...step.toolCalls]); } else { - step.output = { - message: { - content, - role: "assistant", - tool_calls: [...step.toolCalls], + step.output = [ + { + finish_reason: "tool_calls", + index: 0, + message: { + content, + role: "assistant", + tool_calls: [...step.toolCalls], + }, }, - }; + ]; } } - private handleActionResult( + private async handleActionResult( event: Extract, ctx: unknown, hookMetadata?: Record, - ): void { - if (!isToolResult(event.data.result)) { + ): Promise { + if (isToolResult(event.data.result)) { + await this.handleToolResult(event, ctx, event.data.result, hookMetadata); return; } + if (isSubagentResult(event.data.result)) { + await this.handleSubagentResult( + event, + ctx, + event.data.result, + hookMetadata, + ); + } + } + private async handleToolResult( + event: Extract, + ctx: unknown, + result: EveRuntimeToolResultActionResult, + hookMetadata?: Record, + ): Promise { const sessionId = sessionIdFromContext(ctx); if (!sessionId) { return; } - const key = toolKey(sessionId, event.data.result.callId); + const key = toolKey(sessionId, result.callId); + if (this.completedToolKeys.has(key)) { + return; + } const tool = this.toolsByCallKey.get(key) ?? - this.startSyntheticTool(event, ctx, event.data.result, hookMetadata); + (await this.startSyntheticTool(event, ctx, result, hookMetadata)); if (!tool) { return; } const failed = event.data.status === "failed" || - event.data.result.isError === true || + result.isError === true || event.data.error !== undefined; - const metadata = { - ...tool.metadata, - ...(event.data.status ? { "eve.action.status": event.data.status } : {}), - }; - safeLog(tool.span, { + tool.span.log({ ...(failed ? { - error: toLoggedError( - event.data.error?.message ?? event.data.result.output, - ), + error: toLoggedError(event.data.error?.message ?? result.output), } : {}), + metadata: tool.metadata, + output: result.output, + }); + + const turn = this.turnForEvent(event, ctx); + turn?.messages.push({ + content: toolMessageContent(result.output), + name: result.toolName, + role: "tool", + tool_call_id: result.callId, + }); + + const endTime = eventTime(event); + tool.span.end(endTime === undefined ? undefined : { endTime }); + this.toolsByCallKey.delete(key); + this.completedToolKeys.add(key); + } + + private async handleSubagentCalled( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + if (event.data.remote?.url) { + return; + } + + const turn = await this.ensureTurn(event, ctx, hookMetadata); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return; + } + + const key = toolKey(sessionId, event.data.callId); + const metadata = { ...turn.metadata }; + const existing = this.toolsByCallKey.get(key); + if (existing) { + existing.metadata = { ...existing.metadata, ...metadata }; + existing.span.log({ metadata: existing.metadata }); + return; + } + + const [eventId, spanId] = await Promise.all([ + rowIdForSubagent(sessionId, event.data.callId), + spanIdForSubagent(sessionId, event.data.callId), + ]); + const span = startChildSpan(turn.span, { + event: { + id: eventId, + metadata, + }, + name: event.data.toolName ?? event.data.name, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, + startTime: eventTime(event), + }); + span.log({ metadata }); + this.toolsByCallKey.set(key, { + kind: "subagent", metadata, - output: event.data.result.output, + span, + turnKey: turnKey(sessionId, event.data.turnId), + }); + } + + private async handleSubagentCompleted( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + + const key = toolKey(sessionId, event.data.callId); + if (this.completedToolKeys.has(key)) { + return; + } + const subagent = + this.toolsByCallKey.get(key) ?? + (await this.startSyntheticSubagent(event, ctx, hookMetadata)); + if (!subagent) { + return; + } + + this.completeSubagentSpan({ + endTime: eventTime(event), + error: event.data.error, + isError: event.data.status === "failed", + output: event.data.output, + spanState: subagent, + status: event.data.status, + }); + + const turn = this.turnForEvent(event, ctx); + turn?.messages.push({ + content: toolMessageContent(event.data.output), + name: event.data.subagentName, + role: "tool", + tool_call_id: event.data.callId, + }); + + this.toolsByCallKey.delete(key); + this.completedToolKeys.add(key); + } + + private async handleSubagentResult( + event: Extract, + ctx: unknown, + result: Extract, + hookMetadata?: Record, + ): Promise { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + + const key = toolKey(sessionId, result.callId); + if (this.completedToolKeys.has(key)) { + return; + } + const subagent = + this.toolsByCallKey.get(key) ?? + (await this.startSyntheticSubagentResult( + event, + ctx, + result, + hookMetadata, + )); + if (!subagent) { + return; + } + + this.completeSubagentSpan({ + endTime: eventTime(event), + error: event.data.error, + isError: + event.data.status === "failed" || + result.isError === true || + event.data.error !== undefined, + output: result.output, + spanState: subagent, + status: event.data.status, }); const turn = this.turnForEvent(event, ctx); turn?.messages.push({ - content: event.data.result.output, - name: event.data.result.toolName, + content: toolMessageContent(result.output), + name: result.subagentName, role: "tool", - tool_call_id: event.data.result.callId, + tool_call_id: result.callId, }); - safeEnd(tool.span, eventTime(event)); this.toolsByCallKey.delete(key); + this.completedToolKeys.add(key); } private handleStepCompleted( @@ -478,29 +670,79 @@ class EveBridge { return; } - const metrics = metricsFromUsage(event.data.usage); - step.metrics = { ...step.metrics, ...metrics }; - const metadata = { - ...step.metadata, - "eve.finish_reason": event.data.finishReason, - ...(event.data.providerMetadata?.gateway?.generationId - ? { - "eve.gateway.generation_id": - event.data.providerMetadata.gateway.generationId, - } + const usage = event.data.usage; + const inputTokens = + typeof usage?.inputTokens === "number" && + Number.isFinite(usage.inputTokens) && + usage.inputTokens >= 0 + ? usage.inputTokens + : undefined; + const outputTokens = + typeof usage?.outputTokens === "number" && + Number.isFinite(usage.outputTokens) && + usage.outputTokens >= 0 + ? usage.outputTokens + : undefined; + const cacheReadTokens = + typeof usage?.cacheReadTokens === "number" && + Number.isFinite(usage.cacheReadTokens) && + usage.cacheReadTokens >= 0 + ? usage.cacheReadTokens + : undefined; + const cacheWriteTokens = + typeof usage?.cacheWriteTokens === "number" && + Number.isFinite(usage.cacheWriteTokens) && + usage.cacheWriteTokens >= 0 + ? usage.cacheWriteTokens + : undefined; + const costUsd = + typeof usage?.costUsd === "number" && + Number.isFinite(usage.costUsd) && + usage.costUsd >= 0 + ? usage.costUsd + : undefined; + const total = + inputTokens !== undefined && outputTokens !== undefined + ? inputTokens + outputTokens + : undefined; + const metrics = { + ...(inputTokens !== undefined ? { prompt_tokens: inputTokens } : {}), + ...(outputTokens !== undefined + ? { completion_tokens: outputTokens } : {}), + ...(total !== undefined ? { tokens: total } : {}), + ...(cacheReadTokens !== undefined + ? { prompt_cached_tokens: cacheReadTokens } + : {}), + ...(cacheWriteTokens !== undefined + ? { prompt_cache_creation_tokens: cacheWriteTokens } + : {}), + ...(costUsd !== undefined ? { estimated_cost: costUsd } : {}), }; - - safeLog(step.span, { - metadata, + step.metrics = { ...step.metrics, ...metrics }; + if (Array.isArray(step.output) && isObject(step.output[0])) { + const finishReason = Reflect.get(step.output[0], "finish_reason"); + if (typeof finishReason !== "string") { + Reflect.set( + step.output[0], + "finish_reason", + normalizedFinishReason(event.data.finishReason), + ); + } + } + step.span.log({ + metadata: step.metadata, metrics, output: step.output, }); - safeEnd(step.span, eventTime(event)); + const endTime = eventTime(event); + step.span.end(endTime === undefined ? undefined : { endTime }); const turn = this.turnForEvent(event, ctx); if (turn) { - addMetrics(turn.metrics, metrics); + for (const [key, value] of Object.entries(metrics)) { + turn.metrics[key] = (turn.metrics[key] ?? 0) + value; + } turn.stepsByIndex.delete(event.data.stepIndex); } } @@ -519,7 +761,8 @@ class EveBridge { event.data.details, ), ); - safeEnd(step.span, eventTime(event)); + const endTime = eventTime(event); + step.span.end(endTime === undefined ? undefined : { endTime }); } const turn = this.turnForEvent(event, ctx); @@ -536,16 +779,17 @@ class EveBridge { } this.endOpenChildrenForTurn(turn, eventTime(event)); - safeLog(turn.span, { + turn.span.log({ metadata: turn.metadata, metrics: turn.metrics, output: turn.output, }); - safeEnd(turn.span, eventTime(event)); + const endTime = eventTime(event); + turn.span.end(endTime === undefined ? undefined : { endTime }); this.cleanupTurn(event, ctx); void flush().catch((error) => { - logInstrumentationError("Eve flush", error); + debugLogger.warn("Error in Eve flush instrumentation:", error); }); } @@ -563,11 +807,12 @@ class EveBridge { turn.span, errorFromMessage(event.data.message, event.data.code, event.data.details), ); - safeEnd(turn.span, eventTime(event)); + const endTime = eventTime(event); + turn.span.end(endTime === undefined ? undefined : { endTime }); this.cleanupTurn(event, ctx); void flush().catch((error) => { - logInstrumentationError("Eve flush", error); + debugLogger.warn("Error in Eve flush instrumentation:", error); }); } @@ -593,13 +838,15 @@ class EveBridge { event.data.details, ), ); - safeEnd(turn.span, eventTime(event)); + const endTime = eventTime(event); + turn.span.end(endTime === undefined ? undefined : { endTime }); this.turnsByKey.delete(key); } for (const [key, tool] of this.toolsByCallKey) { if (key.startsWith(`${sessionId}:`)) { - safeEnd(tool.span, eventTime(event)); + const endTime = eventTime(event); + tool.span.end(endTime === undefined ? undefined : { endTime }); this.toolsByCallKey.delete(key); } } @@ -607,7 +854,7 @@ class EveBridge { this.sessionsById.delete(sessionId); void flush().catch((error) => { - logInstrumentationError("Eve flush", error); + debugLogger.warn("Error in Eve flush instrumentation:", error); }); } @@ -618,7 +865,7 @@ class EveBridge { } } - private ensureTurn( + private async ensureTurn( event: Extract< EveHandleMessageStreamEvent, { @@ -627,7 +874,7 @@ class EveBridge { >, ctx: unknown, hookMetadata?: Record, - ): TurnState | undefined { + ): Promise { const sessionId = sessionIdFromContext(ctx); if (!sessionId) { return undefined; @@ -639,19 +886,16 @@ class EveBridge { return existing; } - const metadata = this.turnMetadata(sessionId, event, ctx, hookMetadata); - const span = startSpan({ - event: { metadata }, - name: "eve.turn", - spanAttributes: { type: SpanTypeAttribute.TASK }, - startTime: eventTime(event), - }); - safeLog(span, { metadata }); + const metadata = { ...(hookMetadata ?? {}) }; + const span = await this.startTurnSpan(sessionId, event, ctx, metadata); + span.log({ metadata }); const state = { inputLogged: false, + key, messages: [], metadata, metrics: {}, + nextStepOrdinal: 0, span, stepsByIndex: new Map(), }; @@ -659,57 +903,191 @@ class EveBridge { return state; } - private turnForEvent( - event: Extract< - EveHandleMessageStreamEvent, - { data: { readonly turnId: string } } - >, + private async startRequestedTool( + event: Extract, + turn: TurnState, + sessionId: string, + action: EveRuntimeToolCallActionRequest, + step?: StepState, + ): Promise { + const key = toolKey(sessionId, action.callId); + if (this.toolsByCallKey.has(key)) { + return; + } + + const metadata = { ...turn.metadata }; + const [eventId, spanId] = await Promise.all([ + rowIdForTool(sessionId, event.data.turnId, action.callId), + spanIdForTool(sessionId, event.data.turnId, action.callId), + ]); + const span = startChildSpan(turn.span, { + event: { + id: eventId, + input: action.input, + metadata, + }, + name: action.toolName, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, + startTime: eventTime(event), + }); + span.log({ input: action.input, metadata }); + this.toolsByCallKey.set(key, { + kind: "tool", + metadata, + span, + stepIndex: event.data.stepIndex, + stepOrdinal: step?.ordinal, + turnKey: turnKey(sessionId, event.data.turnId), + }); + } + + private async startRequestedSubagent( + event: Extract, + turn: TurnState, + sessionId: string, + action: Extract, + step?: StepState, + ): Promise { + const key = toolKey(sessionId, action.callId); + if (this.toolsByCallKey.has(key)) { + return; + } + + const name = action.subagentName ?? action.name ?? "agent"; + const metadata = { ...turn.metadata }; + const [eventId, spanId] = await Promise.all([ + rowIdForSubagent(sessionId, action.callId), + spanIdForSubagent(sessionId, action.callId), + ]); + const span = startChildSpan(turn.span, { + event: { + id: eventId, + input: action.input, + metadata, + }, + name, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, + startTime: eventTime(event), + }); + span.log({ input: action.input, metadata }); + this.toolsByCallKey.set(key, { + kind: "subagent", + metadata, + span, + stepIndex: event.data.stepIndex, + stepOrdinal: step?.ordinal, + turnKey: turnKey(sessionId, event.data.turnId), + }); + } + + private async startSyntheticTool( + event: Extract, ctx: unknown, - ): TurnState | undefined { + result: EveRuntimeToolResultActionResult, + hookMetadata?: Record, + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); const sessionId = sessionIdFromContext(ctx); - return sessionId - ? this.turnsByKey.get(turnKey(sessionId, event.data.turnId)) - : undefined; + if (!turn || !sessionId) { + return undefined; + } + + const metadata = { ...turn.metadata }; + const [eventId, spanId] = await Promise.all([ + rowIdForTool(sessionId, event.data.turnId, result.callId), + spanIdForTool(sessionId, event.data.turnId, result.callId), + ]); + const span = startChildSpan(turn.span, { + event: { + id: eventId, + metadata, + }, + name: result.toolName, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, + startTime: eventTime(event), + }); + span.log({ metadata }); + const state = { + kind: "tool" as const, + metadata, + span, + stepIndex: event.data.stepIndex, + turnKey: turnKey(sessionId, event.data.turnId), + }; + this.toolsByCallKey.set(toolKey(sessionId, result.callId), state); + return state; } - private stepForEvent( - event: Extract< - EveHandleMessageStreamEvent, - { data: { readonly stepIndex: number; readonly turnId: string } } - >, + private async startSyntheticSubagent( + event: Extract, ctx: unknown, - ): StepState | undefined { - return this.turnForEvent(event, ctx)?.stepsByIndex.get( - event.data.stepIndex, - ); + hookMetadata?: Record, + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return undefined; + } + + const metadata = { ...turn.metadata }; + const [eventId, spanId] = await Promise.all([ + rowIdForSubagent(sessionId, event.data.callId), + spanIdForSubagent(sessionId, event.data.callId), + ]); + const span = startChildSpan(turn.span, { + event: { + id: eventId, + metadata, + }, + name: event.data.subagentName, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, + startTime: eventTime(event), + }); + span.log({ metadata }); + const state = { + kind: "subagent" as const, + metadata, + span, + turnKey: turnKey(sessionId, event.data.turnId), + }; + this.toolsByCallKey.set(toolKey(sessionId, event.data.callId), state); + return state; } - private startSyntheticTool( + private async startSyntheticSubagentResult( event: Extract, ctx: unknown, - result: EveRuntimeToolResultActionResult, + result: Extract, hookMetadata?: Record, - ): ToolState | undefined { - const turn = this.ensureTurn(event, ctx, hookMetadata); + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); const sessionId = sessionIdFromContext(ctx); if (!turn || !sessionId) { return undefined; } - const metadata = { - ...withoutModelMetadata(turn.metadata), - "eve.step.index": event.data.stepIndex, - "eve.tool.call_id": result.callId, - "eve.tool.name": result.toolName, - }; - const span = startEveSpan(turn.span, { - event: { metadata }, - name: result.toolName, + const metadata = { ...turn.metadata }; + const [eventId, spanId] = await Promise.all([ + rowIdForSubagent(sessionId, result.callId), + spanIdForSubagent(sessionId, result.callId), + ]); + const span = startChildSpan(turn.span, { + event: { + id: eventId, + metadata, + }, + name: result.subagentName, spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, startTime: eventTime(event), }); - safeLog(span, { metadata }); + span.log({ metadata }); const state = { + kind: "subagent" as const, metadata, span, stepIndex: event.data.stepIndex, @@ -719,17 +1097,162 @@ class EveBridge { return state; } + private completeSubagentSpan(input: { + endTime: number | undefined; + error?: { readonly message?: string }; + isError: boolean; + output: unknown; + spanState: ToolState; + status?: string; + }): void { + input.spanState.span.log({ + ...(input.isError + ? { error: toLoggedError(input.error?.message ?? input.output) } + : {}), + metadata: input.spanState.metadata, + output: input.output, + }); + input.spanState.span.end( + input.endTime === undefined ? undefined : { endTime: input.endTime }, + ); + } + + private async startTurnSpan( + sessionId: string, + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly sequence: number; readonly turnId: string } } + >, + ctx: unknown, + metadata: Record, + ): Promise { + const lineage = parentLineageFromContext(ctx); + if (lineage) { + await this.recordChildSessionOnParentSubagent(event, ctx, lineage); + } + const parentSubagent = lineage + ? this.toolsByCallKey.get(toolKey(lineage.sessionId, lineage.callId)) + : undefined; + const [eventId, spanId, parentSubagentSpanId, fallbackRootSpanId] = + await Promise.all([ + rowIdForTurn(sessionId, event.data.turnId), + spanIdForTurn(sessionId, event.data.turnId), + lineage + ? spanIdForSubagent(lineage.sessionId, lineage.callId) + : Promise.resolve(undefined), + lineage ? rootSpanIdForLineage(lineage) : Promise.resolve(undefined), + ]); + const rootSpanId = parentSubagent?.span.rootSpanId ?? fallbackRootSpanId; + + return startSpan({ + event: { + id: eventId, + metadata, + }, + name: "eve.turn", + ...(lineage && parentSubagentSpanId && rootSpanId + ? { + parentSpanIds: { + rootSpanId, + spanId: parentSubagentSpanId, + }, + } + : {}), + spanAttributes: { type: SpanTypeAttribute.TASK }, + spanId, + startTime: eventTime(event), + }); + } + + private async recordChildSessionOnParentSubagent( + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly sequence: number; readonly turnId: string } } + >, + ctx: unknown, + lineage: ParentLineage, + ): Promise { + const subagentName = subagentNameFromContext(ctx); + const existing = this.toolsByCallKey.get( + toolKey(lineage.sessionId, lineage.callId), + ); + if (existing) { + return; + } + + if (!lineage.turnId || typeof lineage.turnSequence !== "number") { + return; + } + + const metadata = {}; + const [eventId, spanId, parentTurnSpanId, rootSpanId] = await Promise.all([ + rowIdForSubagent(lineage.sessionId, lineage.callId), + spanIdForSubagent(lineage.sessionId, lineage.callId), + spanIdForTurn(lineage.sessionId, lineage.turnId), + rootSpanIdForLineage(lineage), + ]); + const parentTurn = this.turnsByKey.get( + turnKey(lineage.sessionId, lineage.turnId), + ); + const span = startSpan({ + event: { + id: eventId, + metadata, + }, + name: subagentName ?? "agent", + parentSpanIds: { + rootSpanId: parentTurn?.span.rootSpanId ?? rootSpanId, + spanId: parentTurnSpanId, + }, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, + startTime: eventTime(event), + }); + span.log({ metadata }); + this.toolsByCallKey.set(toolKey(lineage.sessionId, lineage.callId), { + kind: "subagent", + metadata, + span, + turnKey: turnKey(lineage.sessionId, lineage.turnId), + }); + } + + private turnForEvent( + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly turnId: string } } + >, + ctx: unknown, + ): TurnState | undefined { + const sessionId = sessionIdFromContext(ctx); + return sessionId + ? this.turnsByKey.get(turnKey(sessionId, event.data.turnId)) + : undefined; + } + + private stepForEvent( + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly stepIndex: number; readonly turnId: string } } + >, + ctx: unknown, + ): StepState | undefined { + return this.turnForEvent(event, ctx)?.stepsByIndex.get( + event.data.stepIndex, + ); + } + private endOpenChildrenForTurn(turn: TurnState, endTime: number | undefined) { for (const step of turn.stepsByIndex.values()) { - safeEnd(step.span, endTime); + step.span.end(endTime === undefined ? undefined : { endTime }); } turn.stepsByIndex.clear(); for (const [key, tool] of this.toolsByCallKey) { - if (tool.turnKey !== this.keyForTurnState(turn)) { + if (tool.turnKey !== turn.key) { continue; } - safeEnd(tool.span, endTime); + tool.span.end(endTime === undefined ? undefined : { endTime }); this.toolsByCallKey.delete(key); } } @@ -747,67 +1270,25 @@ class EveBridge { } const key = turnKey(sessionId, event.data.turnId); this.turnsByKey.delete(key); - } - private keyForTurnState(turn: TurnState): string | undefined { - const sessionId = turn.metadata["eve.session.id"]; - const turnId = turn.metadata["eve.turn.id"]; - return typeof sessionId === "string" && typeof turnId === "string" - ? turnKey(sessionId, turnId) - : undefined; - } - - private turnMetadata( - sessionId: string, - event: Extract< - EveHandleMessageStreamEvent, - { data: { readonly sequence: number; readonly turnId: string } } - >, - ctx: unknown, - hookMetadata?: Record, - ): Record { - return { - ...baseMetadata({ - channelKind: channelKindFromContext(ctx), - sessionId, - turnId: event.data.turnId, - turnSequence: event.data.sequence, - }), - ...(hookMetadata ?? {}), - ...agentMetadataFromContext(ctx), - ...(this.sessionsById.get(sessionId)?.metadata ?? {}), - }; + for (const completedKey of this.completedToolKeys) { + if (completedKey.startsWith(`${sessionId}:`)) { + this.completedToolKeys.delete(completedKey); + } + } } } -function baseMetadata(input: { - channelKind?: string; - sessionId: string; - turnId: string; - turnSequence: number; -}): Record { - return { - "eve.channel.kind": input.channelKind ?? "unknown", - "eve.session.id": input.sessionId, - "eve.turn.id": input.turnId, - "eve.turn.sequence": input.turnSequence, - }; -} - -function agentMetadataFromContext(ctx: unknown): Record { +function subagentNameFromContext(ctx: unknown): string | undefined { if (!isObject(ctx)) { - return {}; + return undefined; } const agent = Reflect.get(ctx, "agent"); if (!isObject(agent)) { - return {}; + return undefined; } const name = Reflect.get(agent, "name"); - const nodeId = Reflect.get(agent, "nodeId"); - return { - ...(typeof name === "string" ? { "eve.agent.name": name } : {}), - ...(typeof nodeId === "string" ? { "eve.agent.node_id": nodeId } : {}), - }; + return typeof name === "string" ? name : undefined; } function modelMetadataFromRuntime(runtime: unknown): Record { @@ -827,30 +1308,16 @@ function modelMetadataFromModelId(modelId: string): Record { const slashIndex = normalized.indexOf("/"); if (slashIndex > 0 && slashIndex < normalized.length - 1) { return { - "eve.model.id": normalized, model: normalized.slice(slashIndex + 1), provider: normalized.slice(0, slashIndex), }; } return { - "eve.model.id": normalized, model: normalized, }; } -function withoutModelMetadata( - metadata: Record, -): Record { - const { - ["eve.model.id"]: _modelId, - model: _model, - provider: _provider, - ...rest - } = metadata; - return rest; -} - function sessionIdFromContext(ctx: unknown): string | undefined { if (!isObject(ctx)) { return undefined; @@ -863,16 +1330,42 @@ function sessionIdFromContext(ctx: unknown): string | undefined { return typeof id === "string" ? id : undefined; } -function channelKindFromContext(ctx: unknown): string | undefined { +function parentLineageFromContext(ctx: unknown): ParentLineage | undefined { if (!isObject(ctx)) { return undefined; } - const channel = Reflect.get(ctx, "channel"); - if (!isObject(channel)) { + const session = Reflect.get(ctx, "session"); + if (!isObject(session)) { + return undefined; + } + const parent = Reflect.get(session, "parent"); + if (!isObject(parent)) { return undefined; } - const kind = Reflect.get(channel, "kind"); - return typeof kind === "string" ? kind : undefined; + + const callId = Reflect.get(parent, "callId"); + const rootSessionId = Reflect.get(parent, "rootSessionId"); + const sessionId = Reflect.get(parent, "sessionId"); + if ( + typeof callId !== "string" || + typeof rootSessionId !== "string" || + typeof sessionId !== "string" + ) { + return undefined; + } + + const turn = Reflect.get(parent, "turn"); + const turnId = isObject(turn) ? Reflect.get(turn, "id") : undefined; + const turnSequence = isObject(turn) + ? Reflect.get(turn, "sequence") + : undefined; + return { + callId, + rootSessionId, + sessionId, + ...(typeof turnId === "string" ? { turnId } : {}), + ...(typeof turnSequence === "number" ? { turnSequence } : {}), + }; } function isToolCallAction( @@ -887,6 +1380,25 @@ function isToolCallAction( ); } +function isLocalSubagentCallAction( + action: unknown, +): action is Extract { + return ( + isObject(action) && + Reflect.get(action, "kind") === "subagent-call" && + typeof Reflect.get(action, "callId") === "string" && + isObject(Reflect.get(action, "input")) + ); +} + +function isTraceableActionRequest( + action: unknown, +): action is + | EveRuntimeToolCallActionRequest + | Extract { + return isToolCallAction(action) || isLocalSubagentCallAction(action); +} + function isToolResult( result: unknown, ): result is EveRuntimeToolResultActionResult { @@ -898,58 +1410,61 @@ function isToolResult( ); } -function openAIToolCallFromAction(action: EveRuntimeToolCallActionRequest) { +function isSubagentResult( + result: unknown, +): result is Extract { + return ( + isObject(result) && + Reflect.get(result, "kind") === "subagent-result" && + typeof Reflect.get(result, "callId") === "string" && + typeof Reflect.get(result, "subagentName") === "string" + ); +} + +function toolCallMessageFromAction( + action: + | EveRuntimeToolCallActionRequest + | Extract, +): ToolCall { + const name = + action.kind === "tool-call" + ? action.toolName + : (action.subagentName ?? action.name ?? "agent"); return { function: { arguments: JSON.stringify(action.input), - name: action.toolName, + name, }, id: action.callId, - type: "function" as const, + type: "function", }; } -function metricsFromUsage( - usage: Extract< - EveHandleMessageStreamEvent, - { type: "step.completed" } - >["data"]["usage"], -): Record { - if (!usage) { - return {}; +function toolMessageContent(output: unknown): string { + if (typeof output === "string") { + return output; } - - const total = - typeof usage.inputTokens === "number" && - typeof usage.outputTokens === "number" - ? usage.inputTokens + usage.outputTokens - : undefined; - return { - ...(typeof usage.inputTokens === "number" - ? { prompt_tokens: usage.inputTokens } - : {}), - ...(typeof usage.outputTokens === "number" - ? { completion_tokens: usage.outputTokens } - : {}), - ...(typeof total === "number" ? { tokens: total } : {}), - ...(typeof usage.cacheReadTokens === "number" - ? { prompt_cached_tokens: usage.cacheReadTokens } - : {}), - ...(typeof usage.cacheWriteTokens === "number" - ? { prompt_cache_creation_tokens: usage.cacheWriteTokens } - : {}), - ...(typeof usage.costUsd === "number" - ? { estimated_cost: usage.costUsd } - : {}), - }; + try { + const serialized = JSON.stringify(output); + if (typeof serialized === "string") { + return serialized; + } + } catch { + // Fall back to a string representation for unexpected hook payloads. + } + return output === undefined || output === null ? "" : String(output); } -function addMetrics( - target: Record, - metrics: Record, -): void { - for (const [key, value] of Object.entries(metrics)) { - target[key] = (target[key] ?? 0) + value; +function normalizedFinishReason( + finishReason: EveAssistantStepFinishReason, +): string { + switch (finishReason) { + case "content-filter": + return "content_filter"; + case "tool-calls": + return "tool_calls"; + default: + return finishReason; } } @@ -983,26 +1498,101 @@ function toolKey(sessionId: string, callId: string): string { return `${sessionId}:${callId}`; } -function startEveSpan(parent: Span, args: StartSpanArgs): Span { - return withCurrent(parent, () => startSpan(args)); +async function rootSpanIdForSession(sessionId: string): Promise { + return deterministicEveId("eve:root", sessionId); } -function safeLog(span: Span, event: Parameters[0]): void { - try { - span.log(event); - } catch (error) { - logInstrumentationError("Eve span log", error); +async function rootSpanIdForLineage(lineage: ParentLineage): Promise { + if (lineage.turnId) { + return spanIdForTurn(lineage.sessionId, lineage.turnId); } + return rootSpanIdForSession(lineage.rootSessionId); } -function safeEnd(span: Span, endTime: number | undefined): void { - try { - span.end(endTime === undefined ? undefined : { endTime }); - } catch (error) { - logInstrumentationError("Eve span end", error); - } +async function spanIdForTurn( + sessionId: string, + turnId: string, +): Promise { + return deterministicEveId("eve:turn", sessionId, turnId); +} + +async function rowIdForTurn( + sessionId: string, + turnId: string, +): Promise { + return deterministicEveId("eve:row:turn", sessionId, turnId); +} + +async function spanIdForStep( + sessionId: string, + turnId: string, + stepOrdinal: number, +): Promise { + return deterministicEveId("eve:step", sessionId, turnId, String(stepOrdinal)); +} + +async function rowIdForStep( + sessionId: string, + turnId: string, + stepOrdinal: number, +): Promise { + return deterministicEveId( + "eve:row:step", + sessionId, + turnId, + String(stepOrdinal), + ); +} + +async function spanIdForTool( + sessionId: string, + turnId: string, + callId: string, +): Promise { + return deterministicEveId("eve:tool", sessionId, turnId, callId); +} + +async function rowIdForTool( + sessionId: string, + turnId: string, + callId: string, +): Promise { + return deterministicEveId("eve:row:tool", sessionId, turnId, callId); +} + +async function spanIdForSubagent( + sessionId: string, + callId: string, +): Promise { + return deterministicEveId("eve:subagent", sessionId, callId); } -function logInstrumentationError(label: string, error: unknown): void { - debugLogger.warn(`Error in ${label} instrumentation:`, error); +async function rowIdForSubagent( + sessionId: string, + callId: string, +): Promise { + return deterministicEveId("eve:row:subagent", sessionId, callId); +} + +async function deterministicEveId(...parts: string[]): Promise { + const data = new TextEncoder().encode( + parts.map((part) => `${part.length}:${part}`).join("\0"), + ); + const digest = await globalThis.crypto.subtle.digest("SHA-256", data); + const bytes = Array.from(new Uint8Array(digest, 0, 16)); + const hex = bytes.map((byte) => byte.toString(16).padStart(2, "0")).join(""); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; +} + +function startChildSpan( + parent: Span, + args: Parameters[0], +): Span { + return startSpan({ + ...args, + parentSpanIds: { + rootSpanId: parent.rootSpanId, + spanId: parent.spanId, + }, + }); } diff --git a/js/src/vendor-sdk-types/eve.ts b/js/src/vendor-sdk-types/eve.ts index 4d6decfbb..d4f0699db 100644 --- a/js/src/vendor-sdk-types/eve.ts +++ b/js/src/vendor-sdk-types/eve.ts @@ -26,6 +26,15 @@ export interface EveHookContext { }; readonly session?: { readonly id?: string; + readonly parent?: { + readonly callId?: string; + readonly rootSessionId?: string; + readonly sessionId?: string; + readonly turn?: { + readonly id?: string; + readonly sequence?: number; + }; + }; }; } @@ -61,8 +70,15 @@ export type EveRuntimeActionRequest = | { readonly callId: string; readonly input?: EveJsonObject; - readonly kind: "load-skill" | "remote-agent-call" | "subagent-call"; + readonly kind: "load-skill" | "remote-agent-call"; readonly name?: string; + } + | { + readonly callId: string; + readonly input: EveJsonObject; + readonly kind: "subagent-call"; + readonly name?: string; + readonly subagentName?: string; }; export type EveRuntimeActionResult = @@ -70,9 +86,15 @@ export type EveRuntimeActionResult = | { readonly callId: string; readonly isError?: boolean; - readonly kind: "load-skill-result" | "subagent-result"; + readonly kind: "load-skill-result"; readonly output?: EveJsonValue; readonly name?: string; + } + | { + readonly callId: string; + readonly isError?: boolean; + readonly kind: "subagent-result"; + readonly output?: EveJsonValue; readonly subagentName?: string; }; @@ -208,6 +230,34 @@ export type EveHandleMessageStreamEvent = readonly meta?: EveStreamEventMeta; readonly type: "action.result"; } + | { + readonly data: { + readonly callId: string; + readonly childSessionId: string; + readonly name: string; + readonly remote?: { + readonly url?: string; + }; + readonly sequence: number; + readonly toolName?: string; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "subagent.called"; + } + | { + readonly data: { + readonly callId: string; + readonly error?: EveActionResultError; + readonly output?: EveJsonValue; + readonly sequence: number; + readonly status?: EveActionResultStatus; + readonly subagentName: string; + readonly turnId: string; + }; + readonly meta?: EveStreamEventMeta; + readonly type: "subagent.completed"; + } | { readonly data: { readonly code: string; From 83dfd1d0808acb8dcba4b00355e763525cf6f3f5 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 9 Jul 2026 18:15:42 +0200 Subject: [PATCH 05/10] fix race condition --- .../eve-instrumentation/scenario.test.ts | 3 + .../plugins/eve-plugin.test.ts | 90 +++++++++++++++++++ js/src/instrumentation/plugins/eve-plugin.ts | 73 ++++++++++----- 3 files changed, 142 insertions(+), 24 deletions(-) diff --git a/e2e/scenarios/eve-instrumentation/scenario.test.ts b/e2e/scenarios/eve-instrumentation/scenario.test.ts index 8ac331235..7a5a2b6cf 100644 --- a/e2e/scenarios/eve-instrumentation/scenario.test.ts +++ b/e2e/scenarios/eve-instrumentation/scenario.test.ts @@ -89,6 +89,7 @@ describe("eve instrumentation", () => { expect(researcher).toBeDefined(); expect(researcher?.span.type).toBe("tool"); + expect(researcher?.span.ended).toBe(true); expect(researcher?.span.parentIds).toEqual([root?.span.id]); expect(researcher?.input).toMatchObject({ message: expect.stringContaining("Braintrust Eve instrumentation"), @@ -109,6 +110,7 @@ describe("eve instrumentation", () => { expect(childSearch).toBeDefined(); expect(childSearch?.span.type).toBe("tool"); + expect(childSearch?.span.ended).toBe(true); expect(childSearch?.span.parentIds).toEqual([childTurn?.span.id]); expect(childSearch?.input).toMatchObject({ query: expect.stringContaining("Braintrust Eve instrumentation"), @@ -119,6 +121,7 @@ describe("eve instrumentation", () => { expect(read).toBeDefined(); expect(read?.span.type).toBe("tool"); + expect(read?.span.ended).toBe(true); expect(read?.span.parentIds).toEqual([root?.span.id]); expect(read?.input).toMatchObject({ url: "https://eve.dev/docs/guides/instrumentation", diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index fb4bdab43..604137686 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -480,6 +480,96 @@ describe("braintrustEveHook", () => { }); }); + it("merges late tool results into tool spans closed by turn completion", async () => { + const wildcard = braintrustEveHook({ + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-late-tool-result", + }, + }).events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { + agent: { name: "eve-test-agent" }, + channel: { kind: "http" }, + session: { id: "session-late-tool-result" }, + }; + const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); + + await emit({ + data: { sequence: 0, turnId: "turn-late-tool-result" }, + meta: { at: "2026-01-01T00:00:00.000Z" }, + type: "turn.started", + }); + await emit({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-late-tool-result" }, + meta: { at: "2026-01-01T00:00:00.010Z" }, + type: "step.started", + }); + await emit({ + data: { + actions: [ + { + callId: "call-late-search", + input: { query: "late tool result" }, + kind: "tool-call", + toolName: "search", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-late-tool-result", + }, + meta: { at: "2026-01-01T00:00:00.020Z" }, + type: "actions.requested", + }); + await emit({ + data: { sequence: 0, turnId: "turn-late-tool-result" }, + meta: { at: "2026-01-01T00:00:00.030Z" }, + type: "turn.completed", + }); + await emit({ + data: { + result: { + callId: "call-late-search", + kind: "tool-result", + output: { title: "Late result" }, + toolName: "search", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-late-tool-result", + }, + meta: { at: "2026-01-01T00:00:00.040Z" }, + type: "action.result", + }); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const turns = spans.filter( + (span) => span.span_attributes?.name === "eve.turn", + ); + const tool = spans.find((span) => span.span_attributes?.name === "search"); + + expect(turns).toHaveLength(1); + expect(tool).toMatchObject({ + input: { query: "late tool result" }, + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-late-tool-result", + }, + output: { title: "Late result" }, + span_attributes: { + name: "search", + type: "tool", + }, + span_parents: [turns[0]?.span_id], + }); + expect(tool?.metrics?.end).toEqual(expect.any(Number)); + }); + it("uses deterministic ids and nests local subagent sessions from Eve lineage", async () => { const wildcard = braintrustEveHook().events?.["*"]; expect(wildcard).toBeDefined(); diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index 5eb045bd2..f177321c1 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -53,6 +53,7 @@ type TurnState = SpanState & { }; type ToolState = SpanState & { + endedByTurn?: boolean; kind: "subagent" | "tool"; stepIndex?: number; stepOrdinal?: number; @@ -167,13 +168,13 @@ class EveBridge { this.handleStepFailed(event, ctx); return; case "turn.completed": - this.handleTurnCompleted(event, ctx); + await this.handleTurnCompleted(event, ctx); return; case "turn.failed": - this.handleTurnFailed(event, ctx); + await this.handleTurnFailed(event, ctx); return; case "session.failed": - this.handleSessionFailed(event, ctx); + await this.handleSessionFailed(event, ctx); return; case "session.completed": this.handleSessionCompleted(ctx); @@ -492,6 +493,7 @@ class EveBridge { if (!tool) { return; } + const flushAfterCompletion = tool.endedByTurn === true; const failed = event.data.status === "failed" || @@ -519,6 +521,9 @@ class EveBridge { tool.span.end(endTime === undefined ? undefined : { endTime }); this.toolsByCallKey.delete(key); this.completedToolKeys.add(key); + if (flushAfterCompletion) { + await this.flushInstrumentation(); + } } private async handleSubagentCalled( @@ -588,6 +593,7 @@ class EveBridge { if (!subagent) { return; } + const flushAfterCompletion = subagent.endedByTurn === true; this.completeSubagentSpan({ endTime: eventTime(event), @@ -608,6 +614,9 @@ class EveBridge { this.toolsByCallKey.delete(key); this.completedToolKeys.add(key); + if (flushAfterCompletion) { + await this.flushInstrumentation(); + } } private async handleSubagentResult( @@ -636,6 +645,7 @@ class EveBridge { if (!subagent) { return; } + const flushAfterCompletion = subagent.endedByTurn === true; this.completeSubagentSpan({ endTime: eventTime(event), @@ -659,6 +669,9 @@ class EveBridge { this.toolsByCallKey.delete(key); this.completedToolKeys.add(key); + if (flushAfterCompletion) { + await this.flushInstrumentation(); + } } private handleStepCompleted( @@ -769,10 +782,10 @@ class EveBridge { turn?.stepsByIndex.delete(event.data.stepIndex); } - private handleTurnCompleted( + private async handleTurnCompleted( event: Extract, ctx: unknown, - ): void { + ): Promise { const turn = this.turnForEvent(event, ctx); if (!turn) { return; @@ -788,15 +801,13 @@ class EveBridge { turn.span.end(endTime === undefined ? undefined : { endTime }); this.cleanupTurn(event, ctx); - void flush().catch((error) => { - debugLogger.warn("Error in Eve flush instrumentation:", error); - }); + await this.flushInstrumentation(); } - private handleTurnFailed( + private async handleTurnFailed( event: Extract, ctx: unknown, - ): void { + ): Promise { const turn = this.turnForEvent(event, ctx); if (!turn) { return; @@ -811,15 +822,13 @@ class EveBridge { turn.span.end(endTime === undefined ? undefined : { endTime }); this.cleanupTurn(event, ctx); - void flush().catch((error) => { - debugLogger.warn("Error in Eve flush instrumentation:", error); - }); + await this.flushInstrumentation(); } - private handleSessionFailed( + private async handleSessionFailed( event: Extract, ctx: unknown, - ): void { + ): Promise { const sessionId = event.data.sessionId || sessionIdFromContext(ctx); if (!sessionId) { return; @@ -853,15 +862,23 @@ class EveBridge { this.sessionsById.delete(sessionId); - void flush().catch((error) => { - debugLogger.warn("Error in Eve flush instrumentation:", error); - }); + await this.flushInstrumentation(); } private handleSessionCompleted(ctx: unknown): void { const sessionId = sessionIdFromContext(ctx); if (sessionId) { this.sessionsById.delete(sessionId); + for (const key of this.toolsByCallKey.keys()) { + if (key.startsWith(`${sessionId}:`)) { + this.toolsByCallKey.delete(key); + } + } + for (const completedKey of this.completedToolKeys) { + if (completedKey.startsWith(`${sessionId}:`)) { + this.completedToolKeys.delete(completedKey); + } + } } } @@ -1244,16 +1261,22 @@ class EveBridge { private endOpenChildrenForTurn(turn: TurnState, endTime: number | undefined) { for (const step of turn.stepsByIndex.values()) { + step.span.log({ + metadata: step.metadata, + metrics: step.metrics, + output: step.output, + }); step.span.end(endTime === undefined ? undefined : { endTime }); } turn.stepsByIndex.clear(); - for (const [key, tool] of this.toolsByCallKey) { + for (const tool of this.toolsByCallKey.values()) { if (tool.turnKey !== turn.key) { continue; } + tool.span.log({ metadata: tool.metadata }); tool.span.end(endTime === undefined ? undefined : { endTime }); - this.toolsByCallKey.delete(key); + tool.endedByTurn = true; } } @@ -1270,11 +1293,13 @@ class EveBridge { } const key = turnKey(sessionId, event.data.turnId); this.turnsByKey.delete(key); + } - for (const completedKey of this.completedToolKeys) { - if (completedKey.startsWith(`${sessionId}:`)) { - this.completedToolKeys.delete(completedKey); - } + private async flushInstrumentation(): Promise { + try { + await flush(); + } catch (error) { + debugLogger.warn("Error in Eve flush instrumentation:", error); } } } From bf0c37442d1bb616886a1a71d4be15fa4001d71d Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 9 Jul 2026 19:36:36 +0200 Subject: [PATCH 06/10] we love input --- .../eve-instrumentation.span-tree.json | 294 +++++--- .../eve-instrumentation.span-tree.txt | 294 +++++--- .../agent/instrumentation.ts | 18 +- .../eve-instrumentation/scenario.test.ts | 20 +- js/src/exports.ts | 5 +- js/src/instrumentation/index.ts | 5 +- .../plugins/eve-plugin.test.ts | 451 +++++++++-- js/src/instrumentation/plugins/eve-plugin.ts | 707 ++++++++++++------ js/src/vendor-sdk-types/eve.ts | 40 + 9 files changed, 1361 insertions(+), 473 deletions(-) diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json index a3f131a79..78cfb22ed 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -8,12 +8,15 @@ "name": "eve.step", "type": "llm", "children": [], - "input": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ], + "input": { + "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "messages": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ] + }, "output": [ { "finish_reason": "tool_calls", @@ -59,12 +62,15 @@ "name": "eve.step", "type": "llm", "children": [], - "input": [ - { - "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ], + "input": { + "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "messages": [ + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ] + }, "output": [ { "finish_reason": "tool_calls", @@ -119,32 +125,56 @@ "name": "eve.step", "type": "llm", "children": [], - "input": [ - { - "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "search" - }, - "id": "", - "type": "function" - } - ] - }, - { - "content": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - "name": "search", - "role": "tool", - "tool_call_id": "call_bsPk0zM9ENF7o5XXNuEmMjhA" - } - ], + "input": { + "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "messages": [ + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "query": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-result" + } + ], + "role": "tool" + } + ] + }, "output": [ { "finish_reason": "stop", @@ -177,6 +207,8 @@ ], "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" }, @@ -201,26 +233,47 @@ "name": "eve.step", "type": "llm", "children": [], - "input": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "researcher" - }, - "id": "", - "type": "function" - } - ] - } - ], + "input": { + "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "messages": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + } + ] + }, "output": [ { "finish_reason": "tool_calls", @@ -276,46 +329,89 @@ "name": "eve.step", "type": "llm", "children": [], - "input": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "researcher" - }, - "id": "", - "type": "function" - } - ] - }, - { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - "name": "read" - }, - "id": "", - "type": "function" - } - ] - }, - { - "content": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - "name": "read", - "role": "tool", - "tool_call_id": "call_yRc6f1bJPthZhaIEzCySgUi0" - } - ], + "input": { + "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "messages": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + } + ] + }, "output": [ { "finish_reason": "stop", @@ -348,6 +444,8 @@ ], "output": "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" }, diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt index 0d56dd2aa..7dd3062e3 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -8,6 +8,8 @@ span_tree: ] output: "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace." metadata: { + "model": "gpt-5.4-mini", + "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" } @@ -18,12 +20,15 @@ span_tree: "tokens": 20317 } ├── eve.step [llm] - │ input: [ - │ { - │ "content": "Run the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ } - │ ] + │ input: { + │ "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "messages": [ + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ } + │ ] + │ } │ output: [ │ { │ "finish_reason": "tool_calls", @@ -74,6 +79,8 @@ span_tree: │ ] │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", │ "scenario": "eve-instrumentation", │ "testRunId": "" │ } @@ -84,12 +91,15 @@ span_tree: │ "tokens": 13226 │ } │ ├── eve.step [llm] - │ │ input: [ - │ │ { - │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - │ │ "role": "user" - │ │ } - │ │ ] + │ │ input: { + │ │ "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ │ "messages": [ + │ │ { + │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ │ "role": "user" + │ │ } + │ │ ] + │ │ } │ │ output: [ │ │ { │ │ "finish_reason": "tool_calls", @@ -136,32 +146,56 @@ span_tree: │ │ "testRunId": "" │ │ } │ └── eve.step [llm] - │ input: [ - │ { - │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ }, - │ { - │ "content": null, - │ "role": "assistant", - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - │ "name": "search" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] - │ }, - │ { - │ "content": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - │ "name": "search", - │ "role": "tool", - │ "tool_call_id": "call_bsPk0zM9ENF7o5XXNuEmMjhA" - │ } - │ ] + │ input: { + │ "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "messages": [ + │ { + │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "search", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "json", + │ "value": { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "search", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ } + │ ] + │ } │ output: [ │ { │ "finish_reason": "stop", @@ -185,26 +219,47 @@ span_tree: │ "tokens": 6646 │ } ├── eve.step [llm] - │ input: [ - │ { - │ "content": "Run the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ }, - │ { - │ "content": null, - │ "role": "assistant", - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - │ "name": "researcher" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] - │ } - │ ] + │ input: { + │ "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "messages": [ + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "text", + │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ } + │ ] + │ } │ output: [ │ { │ "finish_reason": "tool_calls", @@ -252,46 +307,89 @@ span_tree: │ "testRunId": "" │ } └── eve.step [llm] - input: [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "researcher" - }, - "id": "", - "type": "function" - } - ] - }, - { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - "name": "read" - }, - "id": "", - "type": "function" - } - ] - }, - { - "content": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - "name": "read", - "role": "tool", - "tool_call_id": "call_yRc6f1bJPthZhaIEzCySgUi0" - } - ] + input: { + "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "messages": [ + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + } + ] + } output: [ { "finish_reason": "stop", diff --git a/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts b/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts index 297f3555f..6ca571b23 100644 --- a/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts +++ b/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts @@ -1,10 +1,12 @@ -import { initLogger } from "braintrust"; +import { braintrustEveInstrumentation, initLogger } from "braintrust"; import { defineInstrumentation } from "eve/instrumentation"; -export default defineInstrumentation({ - setup: ({ agentName }) => { - initLogger({ - projectName: process.env.BRAINTRUST_E2E_PROJECT_NAME || agentName, - }); - }, -}); +export default defineInstrumentation( + braintrustEveInstrumentation({ + setup: ({ agentName }) => { + initLogger({ + projectName: process.env.BRAINTRUST_E2E_PROJECT_NAME || agentName, + }); + }, + }), +); diff --git a/e2e/scenarios/eve-instrumentation/scenario.test.ts b/e2e/scenarios/eve-instrumentation/scenario.test.ts index 7a5a2b6cf..b7737f335 100644 --- a/e2e/scenarios/eve-instrumentation/scenario.test.ts +++ b/e2e/scenarios/eve-instrumentation/scenario.test.ts @@ -55,6 +55,7 @@ describe("eve instrumentation", () => { "eve.turn", researcher?.span.id, )[0]; + const childSteps = findChildSpans(events, "eve.step", childTurn?.span.id); const childSearch = findLatestChildSpan( events, "search", @@ -78,7 +79,9 @@ describe("eve instrumentation", () => { expect(steps.map((step) => step.span.type)).toEqual(["llm", "llm", "llm"]); for (const step of steps) { expect(step.span.parentIds).toEqual([root?.span.id]); - expect(step.input).toEqual(expect.arrayContaining([expect.anything()])); + expect(step.input).toMatchObject({ + messages: expect.any(Array), + }); expect(step.metadata).toMatchObject({ model: "gpt-5.4-mini", provider: "openai", @@ -108,6 +111,21 @@ describe("eve instrumentation", () => { testRunId: expect.any(String), }); + expect(childSteps).toHaveLength(2); + for (const step of childSteps) { + expect(step.span.type).toBe("llm"); + expect(step.span.parentIds).toEqual([childTurn?.span.id]); + expect(step.input).toMatchObject({ + messages: expect.any(Array), + }); + expect(step.metadata).toMatchObject({ + model: "gpt-5.4-mini", + provider: "openai", + scenario: "eve-instrumentation", + testRunId: expect.any(String), + }); + } + expect(childSearch).toBeDefined(); expect(childSearch?.span.type).toBe("tool"); expect(childSearch?.span.ended).toBe(true); diff --git a/js/src/exports.ts b/js/src/exports.ts index a2b0a38f6..e3274941a 100644 --- a/js/src/exports.ts +++ b/js/src/exports.ts @@ -175,7 +175,10 @@ export { BraintrustMiddleware, wrapAISDKModel, } from "./wrappers/ai-sdk"; -export { braintrustEveHook } from "./instrumentation/plugins/eve-plugin"; +export { + braintrustEveHook, + braintrustEveInstrumentation, +} from "./instrumentation/plugins/eve-plugin"; export { wrapAnthropic } from "./wrappers/anthropic"; export { BraintrustObservabilityExporter, diff --git a/js/src/instrumentation/index.ts b/js/src/instrumentation/index.ts index 0c0d4f9db..f398abbdb 100644 --- a/js/src/instrumentation/index.ts +++ b/js/src/instrumentation/index.ts @@ -23,7 +23,10 @@ export { braintrustFlueInstrumentation, braintrustFlueObserver, } from "./plugins/flue-plugin"; -export { braintrustEveHook } from "./plugins/eve-plugin"; +export { + braintrustEveHook, + braintrustEveInstrumentation, +} from "./plugins/eve-plugin"; // Re-export core types for external instrumentation packages export type { diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index 604137686..cbd2dcac2 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -1,5 +1,14 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; import { createHash } from "node:crypto"; +import * as braintrustExports from "../../exports"; import { configureNode } from "../../node/config"; import { _exportsForTestingOnly, @@ -7,7 +16,8 @@ import { startSpan, withCurrent, } from "../../logger"; -import { braintrustEveHook } from "./eve-plugin"; +import * as instrumentationExports from "../index"; +import { braintrustEveHook, braintrustEveInstrumentation } from "./eve-plugin"; import type { EveHandleMessageStreamEvent, EveHookContext, @@ -21,6 +31,55 @@ function deterministicEveIdForTest(...parts: string[]): string { .replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, "$1-$2-$3-$4-$5"); } +const EVE_CONTEXT_KEY_REGISTRY = Symbol.for("eve.context-key-registry"); +const EVE_CONTEXT_STORAGE = Symbol.for("eve.context-storage"); +const EVE_LLM_INPUT_STATE_KEY = "braintrust.eve.llmInputs"; + +function installFakeEveContext() { + const hadRegistry = Reflect.has(globalThis, EVE_CONTEXT_KEY_REGISTRY); + const hadStorage = Reflect.has(globalThis, EVE_CONTEXT_STORAGE); + const previousRegistry = Reflect.get(globalThis, EVE_CONTEXT_KEY_REGISTRY); + const previousStorage = Reflect.get(globalThis, EVE_CONTEXT_STORAGE); + const values = new Map(); + const keyName = (key: unknown) => + key && typeof key === "object" && "name" in key + ? Reflect.get(key, "name") + : undefined; + const context = { + get(key: unknown) { + const name = keyName(key); + return typeof name === "string" ? values.get(name) : undefined; + }, + set(key: unknown, value: unknown) { + const name = keyName(key); + if (typeof name === "string") { + values.set(name, value); + } + }, + }; + + Reflect.set(globalThis, EVE_CONTEXT_KEY_REGISTRY, new Map()); + Reflect.set(globalThis, EVE_CONTEXT_STORAGE, { + getStore: () => context, + }); + + return { + values, + restore() { + if (hadRegistry) { + Reflect.set(globalThis, EVE_CONTEXT_KEY_REGISTRY, previousRegistry); + } else { + Reflect.deleteProperty(globalThis, EVE_CONTEXT_KEY_REGISTRY); + } + if (hadStorage) { + Reflect.set(globalThis, EVE_CONTEXT_STORAGE, previousStorage); + } else { + Reflect.deleteProperty(globalThis, EVE_CONTEXT_STORAGE); + } + }, + }; +} + try { configureNode(); } catch { @@ -31,6 +90,7 @@ describe("braintrustEveHook", () => { let backgroundLogger: ReturnType< typeof _exportsForTestingOnly.useTestBackgroundLogger >; + let restoreFakeEveContext: (() => void) | undefined; beforeAll(async () => { await _exportsForTestingOnly.simulateLoginForTests(); @@ -46,6 +106,9 @@ describe("braintrustEveHook", () => { }); afterEach(() => { + vi.restoreAllMocks(); + restoreFakeEveContext?.(); + restoreFakeEveContext = undefined; Reflect.deleteProperty(globalThis, Symbol.for("braintrust.eve.bridge")); _exportsForTestingOnly.clearTestBackgroundLogger(); }); @@ -57,6 +120,167 @@ describe("braintrustEveHook", () => { expect(typeof hook.events?.["*"]).toBe("function"); }); + it("returns an Eve instrumentation definition", () => { + const setup = vi.fn(); + const instrumentation = braintrustEveInstrumentation({ setup }); + + expect(instrumentation).toMatchObject({ + recordInputs: false, + recordOutputs: false, + setup, + }); + expect(typeof instrumentation.events?.["step.started"]).toBe("function"); + }); + + it("exports Eve APIs from root and instrumentation entrypoints", () => { + expect(braintrustExports.braintrustEveHook).toBe(braintrustEveHook); + expect(braintrustExports.braintrustEveInstrumentation).toBe( + braintrustEveInstrumentation, + ); + expect(instrumentationExports.braintrustEveHook).toBe(braintrustEveHook); + expect(instrumentationExports.braintrustEveInstrumentation).toBe( + braintrustEveInstrumentation, + ); + }); + + it("captures Eve instrumentation model input on the matching LLM step", async () => { + const fakeEve = installFakeEveContext(); + restoreFakeEveContext = fakeEve.restore; + const instrumentation = braintrustEveInstrumentation(); + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { + session: { id: "session-captured-input" }, + }; + const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); + const modelInput = { + instructions: "Answer with the relevant Eve instrumentation detail.", + messages: [ + { + content: "What should Braintrust capture?", + role: "user", + }, + ], + }; + + await emit({ + data: { sequence: 0, turnId: "turn-captured-input" }, + type: "turn.started", + }); + instrumentation.events?.["step.started"]?.({ + modelInput, + session: { id: "session-captured-input" }, + step: { index: 0 }, + turn: { id: "turn-captured-input", sequence: 0 }, + }); + await emit({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-captured-input" }, + type: "step.started", + }); + await emit({ + data: { + finishReason: "stop", + message: "Capture the model input.", + sequence: 0, + stepIndex: 0, + turnId: "turn-captured-input", + }, + type: "message.completed", + }); + await emit({ + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 0, + turnId: "turn-captured-input", + }, + type: "step.completed", + }); + await emit({ + data: { sequence: 0, turnId: "turn-captured-input" }, + type: "turn.completed", + }); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const step = spans.find( + (span) => span.span_attributes?.name === "eve.step", + ); + expect(step?.input).toEqual(modelInput); + expect(fakeEve.values.get(EVE_LLM_INPUT_STATE_KEY)).toEqual({ + entries: [], + }); + }); + + it("skips missing or malformed Eve instrumentation state without throwing", async () => { + const instrumentation = braintrustEveInstrumentation(); + expect(() => + instrumentation.events?.["step.started"]?.({ bad: true } as never), + ).not.toThrow(); + + const fakeEve = installFakeEveContext(); + restoreFakeEveContext = fakeEve.restore; + fakeEve.values.set(EVE_LLM_INPUT_STATE_KEY, { + entries: [{ input: { messages: "not an array" }, key: "bad" }], + }); + expect(() => + instrumentation.events?.["step.started"]?.({ + modelInput: { + messages: [{ content: "hello", role: "user" }], + }, + session: { id: "session-malformed-state" }, + step: { index: 0 }, + turn: { id: "turn-malformed-state" }, + }), + ).not.toThrow(); + + const wildcard = braintrustEveHook().events?.["*"]; + const ctx: EveHookContext = { session: { id: "session-no-state" } }; + await wildcard?.( + { + data: { sequence: 0, turnId: "turn-no-state" }, + type: "turn.started", + }, + ctx, + ); + await wildcard?.( + { + data: { sequence: 0, stepIndex: 0, turnId: "turn-no-state" }, + type: "step.started", + }, + ctx, + ); + await wildcard?.( + { + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 0, + turnId: "turn-no-state", + }, + type: "step.completed", + }, + ctx, + ); + await wildcard?.( + { + data: { sequence: 0, turnId: "turn-no-state" }, + type: "turn.completed", + }, + ctx, + ); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const step = spans.find( + (span) => span.span_attributes?.name === "eve.step", + ); + expect(step?.input).toBeUndefined(); + }); + it("records a flat Eve turn with session model metadata", async () => { const wildcard = braintrustEveHook({ metadata: { @@ -215,6 +439,7 @@ describe("braintrustEveHook", () => { expect(root).toMatchObject({ input: [{ content: "Search then read", role: "user" }], metadata: { + ...expectedModelMetadata, scenario: "eve-plugin-unit", testRunId: "test-run-flat-tree", }, @@ -252,32 +477,8 @@ describe("braintrustEveHook", () => { testRunId: "test-run-flat-tree", }); } - expect(steps[0]?.input).toEqual([ - { content: "Search then read", role: "user" }, - ]); - expect(steps[1]?.input).toMatchObject([ - { content: "Search then read", role: "user" }, - { - content: null, - role: "assistant", - tool_calls: [ - { - function: { - arguments: JSON.stringify({ query: "Eve instrumentation" }), - name: "search", - }, - id: "call-search", - type: "function", - }, - ], - }, - { - content: JSON.stringify({ hits: ["eve.dev/docs"] }), - name: "search", - role: "tool", - tool_call_id: "call-search", - }, - ]); + expect(steps[0]?.input).toBeUndefined(); + expect(steps[1]?.input).toBeUndefined(); expect(tool).toMatchObject({ input: { query: "Eve instrumentation" }, metadata: { @@ -321,7 +522,7 @@ describe("braintrustEveHook", () => { ]); }); - it("merges incremental tool call requests into one assistant message", async () => { + it("does not reconstruct later LLM inputs from earlier hook events", async () => { const wildcard = braintrustEveHook().events?.["*"]; expect(wildcard).toBeDefined(); @@ -458,26 +659,11 @@ describe("braintrustEveHook", () => { { finish_reason: "tool_calls", message: { - tool_calls: [ - { id: "call-search", function: { name: "search" } }, - { id: "call-read", function: { name: "read" } }, - ], + tool_calls: [{ id: "call-read", function: { name: "read" } }], }, }, ]); - const assistantToolMessages = steps[1]?.input.filter( - (message: Record) => - message.role === "assistant" && Array.isArray(message.tool_calls), - ); - expect(assistantToolMessages).toHaveLength(1); - expect(assistantToolMessages?.[0]).toMatchObject({ - content: null, - role: "assistant", - tool_calls: [ - { id: "call-search", function: { name: "search" }, type: "function" }, - { id: "call-read", function: { name: "read" }, type: "function" }, - ], - }); + expect(steps[1]?.input).toBeUndefined(); }); it("merges late tool results into tool spans closed by turn completion", async () => { @@ -570,6 +756,179 @@ describe("braintrustEveHook", () => { expect(tool?.metrics?.end).toEqual(expect.any(Number)); }); + it("keeps deterministic tool state available after session completion", async () => { + const wildcard = braintrustEveHook({ + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-late-after-session", + }, + }).events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { + agent: { name: "eve-test-agent" }, + channel: { kind: "http" }, + session: { id: "session-late-after-session" }, + }; + const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); + + await emit({ + data: { sequence: 0, turnId: "turn-late-after-session" }, + meta: { at: "2026-01-01T00:00:00.000Z" }, + type: "turn.started", + }); + await emit({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-late-after-session" }, + meta: { at: "2026-01-01T00:00:00.010Z" }, + type: "step.started", + }); + await emit({ + data: { + actions: [ + { + callId: "call-after-session", + input: { query: "after session" }, + kind: "tool-call", + toolName: "search", + }, + ], + sequence: 0, + stepIndex: 0, + turnId: "turn-late-after-session", + }, + meta: { at: "2026-01-01T00:00:00.020Z" }, + type: "actions.requested", + }); + await emit({ + meta: { at: "2026-01-01T00:00:00.030Z" }, + type: "session.completed", + }); + await emit({ + data: { + result: { + callId: "call-after-session", + kind: "tool-result", + output: { title: "After session" }, + toolName: "search", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-late-after-session", + }, + meta: { at: "2026-01-01T00:00:00.040Z" }, + type: "action.result", + }); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const tool = spans.find((span) => span.span_attributes?.name === "search"); + expect(tool).toMatchObject({ + input: { query: "after session" }, + output: { title: "After session" }, + }); + expect(tool?.metrics?.end).toEqual(expect.any(Number)); + }); + + it("records result-only tool events without invented input", async () => { + const wildcard = braintrustEveHook({ + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-result-only", + }, + }).events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { + agent: { name: "eve-test-agent" }, + channel: { kind: "http" }, + session: { id: "session-result-only" }, + }; + const emit = (event: EveHandleMessageStreamEvent) => wildcard?.(event, ctx); + + await emit({ + data: { + result: { + callId: "call-result-only", + kind: "tool-result", + output: { title: "Result only" }, + toolName: "search", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-result-only", + }, + type: "action.result", + }); + await emit({ + data: { sequence: 0, turnId: "turn-result-only" }, + type: "turn.completed", + }); + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const tool = spans.find((span) => span.span_attributes?.name === "search"); + expect(tool).toMatchObject({ + output: { title: "Result only" }, + span_attributes: { name: "search", type: "tool" }, + }); + expect(tool?.input).toBeUndefined(); + }); + + it("serializes events per session without blocking other sessions", async () => { + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + const sessionA: EveHookContext = { session: { id: "session-queue-a" } }; + const sessionB: EveHookContext = { session: { id: "session-queue-b" } }; + const emitA = (event: EveHandleMessageStreamEvent) => + wildcard?.(event, sessionA); + const emitB = (event: EveHandleMessageStreamEvent) => + wildcard?.(event, sessionB); + + await emitA({ + data: { sequence: 0, turnId: "turn-a" }, + type: "turn.started", + }); + await emitB({ + data: { sequence: 0, turnId: "turn-b" }, + type: "turn.started", + }); + + let releaseFirstFlush: (() => void) | undefined; + const firstFlush = new Promise((resolve) => { + releaseFirstFlush = resolve; + }); + const flushSpy = vi + .spyOn(backgroundLogger, "flush") + .mockImplementationOnce(() => firstFlush) + .mockResolvedValue(undefined); + + const doneA = emitA({ + data: { sequence: 0, turnId: "turn-a" }, + type: "turn.completed", + }); + for (let i = 0; i < 10 && flushSpy.mock.calls.length < 1; i++) { + await new Promise((resolve) => setTimeout(resolve, 0)); + } + expect(flushSpy).toHaveBeenCalledTimes(1); + + const doneB = emitB({ + data: { sequence: 0, turnId: "turn-b" }, + type: "turn.completed", + }); + for (let i = 0; i < 10 && flushSpy.mock.calls.length < 2; i++) { + await new Promise((resolve) => setTimeout(resolve, 0)); + } + expect(flushSpy).toHaveBeenCalledTimes(2); + + releaseFirstFlush?.(); + await Promise.all([doneA, doneB]); + }); + it("uses deterministic ids and nests local subagent sessions from Eve lineage", async () => { const wildcard = braintrustEveHook().events?.["*"]; expect(wildcard).toBeDefined(); diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index f177321c1..5d6428028 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -8,6 +8,8 @@ import type { EveHandleMessageStreamEvent, EveHookContext, EveHookDefinition, + EveInstrumentationDefinition, + EveInstrumentationStepStartedEventInput, EveRuntimeActionRequest, EveRuntimeActionResult, EveRuntimeToolCallActionRequest, @@ -19,33 +21,15 @@ type SpanState = { span: Span; }; -type ToolCall = { - function: { - arguments: string; - name: string; - }; - id: string; - type: "function"; -}; - -type AssistantToolCallMessage = { - content: unknown; - role: "assistant"; - tool_calls: ToolCall[]; -}; - type StepState = SpanState & { - assistantToolCallMessage?: AssistantToolCallMessage; + input?: unknown; metrics: Record; ordinal: number; output?: unknown; - toolCalls: ToolCall[]; }; type TurnState = SpanState & { - inputLogged: boolean; key: string; - messages: unknown[]; metrics: Record; nextStepOrdinal: number; output?: unknown; @@ -69,6 +53,23 @@ type ParentLineage = { }; const EVE_BRIDGE = Symbol.for("braintrust.eve.bridge"); +const EVE_CONTEXT_KEY_REGISTRY = Symbol.for("eve.context-key-registry"); +const EVE_CONTEXT_STORAGE = Symbol.for("eve.context-storage"); +const EVE_LLM_INPUT_STATE_KEY = "braintrust.eve.llmInputs"; +const EVE_LLM_INPUT_STATE = { name: EVE_LLM_INPUT_STATE_KEY }; +const MAX_STORED_LLM_INPUTS = 100; + +type CapturedEveModelInput = { + instructions?: string | readonly unknown[]; + messages: readonly unknown[]; +}; + +type EveLlmInputState = { + entries: readonly { + input: CapturedEveModelInput; + key: string; + }[]; +}; /** Manual hook instrumentation for eve runtime stream events. */ export function braintrustEveHook( @@ -83,23 +84,58 @@ export function braintrustEveHook( }; } +/** Eve instrumentation helper for logger setup and durable LLM input capture. */ +export function braintrustEveInstrumentation( + options: { + setup?: EveInstrumentationDefinition["setup"]; + } = {}, +): EveInstrumentationDefinition { + return { + events: { + "step.started": (input: EveInstrumentationStepStartedEventInput) => { + try { + captureEveModelInput(input); + } catch (error) { + debugLogger.warn("Error in Eve LLM input capture:", error); + } + }, + }, + recordInputs: false, + recordOutputs: false, + setup: options.setup, + }; +} + function getEveBridge(): EveBridge { - const existing = Reflect.get(globalThis, EVE_BRIDGE); + const existing = Object.getOwnPropertyDescriptor( + globalThis, + EVE_BRIDGE, + )?.value; if (existing instanceof EveBridge) { return existing; } const bridge = new EveBridge(); - Reflect.set(globalThis, EVE_BRIDGE, bridge); + Object.defineProperty(globalThis, EVE_BRIDGE, { + configurable: true, + value: bridge, + writable: true, + }); return bridge; } +function isEveHandleMessageStreamEvent( + event: unknown, +): event is EveHandleMessageStreamEvent { + return isObject(event) && typeof event["type"] === "string"; +} + class EveBridge { private sessionsById = new Map< string, { metadata: Record } >(); private completedToolKeys = new Set(); - private queue = Promise.resolve(); + private queuesBySessionId = new Map>(); private toolsByCallKey = new Map(); private turnsByKey = new Map(); @@ -108,20 +144,21 @@ class EveBridge { ctx: unknown, hookMetadata?: Record, ): Promise { - if (!isObject(event)) { + if (!isEveHandleMessageStreamEvent(event)) { return Promise.resolve(); } - const next = this.queue - .then(() => - this.handleEvent( - event as EveHandleMessageStreamEvent, - ctx, - hookMetadata, - ), - ) + const queueKey = queueKeyForEvent(event, ctx); + const previous = this.queuesBySessionId.get(queueKey) ?? Promise.resolve(); + const next = previous + .then(() => this.handleEvent(event, ctx, hookMetadata)) .catch(() => undefined); - this.queue = next; + this.queuesBySessionId.set(queueKey, next); + void next.finally(() => { + if (this.queuesBySessionId.get(queueKey) === next) { + this.queuesBySessionId.delete(queueKey); + } + }); return next; } @@ -177,7 +214,7 @@ class EveBridge { await this.handleSessionFailed(event, ctx); return; case "session.completed": - this.handleSessionCompleted(ctx); + await this.handleSessionCompleted(event, ctx); return; default: return; @@ -200,6 +237,8 @@ class EveBridge { continue; } + turn.metadata = { ...turn.metadata, ...metadata }; + turn.span.log({ metadata: turn.metadata }); for (const step of turn.stepsByIndex.values()) { step.metadata = { ...step.metadata, ...metadata }; step.span.log({ metadata: step.metadata }); @@ -218,20 +257,21 @@ class EveBridge { } const key = turnKey(sessionId, event.data.turnId); - const metadata = { ...(hookMetadata ?? {}) }; + const metadata = { + ...(hookMetadata ?? {}), + ...(this.sessionsById.get(sessionId)?.metadata ?? {}), + }; const existing = this.turnsByKey.get(key); if (existing) { - existing.metadata = metadata; - existing.span.log({ metadata }); + existing.metadata = { ...existing.metadata, ...metadata }; + existing.span.log({ metadata: existing.metadata }); return; } const span = await this.startTurnSpan(sessionId, event, ctx, metadata); span.log({ metadata }); this.turnsByKey.set(key, { - inputLogged: false, key, - messages: [], metadata, metrics: {}, nextStepOrdinal: 0, @@ -251,11 +291,7 @@ class EveBridge { } const input = [{ content: event.data.message, role: "user" }]; - turn.messages.push(...input); - if (!turn.inputLogged) { - turn.inputLogged = true; - turn.span.log({ input }); - } + turn.span.log({ input }); } private async handleStepStarted( @@ -272,6 +308,7 @@ class EveBridge { const existing = turn.stepsByIndex.get(event.data.stepIndex); if (existing) { existing.span.log({ + ...(existing.input !== undefined ? { input: existing.input } : {}), metadata: existing.metadata, metrics: existing.metrics, output: existing.output, @@ -285,7 +322,11 @@ class EveBridge { ...turn.metadata, ...(this.sessionsById.get(sessionId)?.metadata ?? {}), }; - const input = turn.messages.length > 0 ? [...turn.messages] : undefined; + const input = consumeCapturedEveModelInput( + sessionId, + event.data.turnId, + event.data.stepIndex, + ); const [eventId, spanId] = await Promise.all([ rowIdForStep(sessionId, event.data.turnId, stepOrdinal), spanIdForStep(sessionId, event.data.turnId, stepOrdinal), @@ -293,7 +334,7 @@ class EveBridge { const span = startChildSpan(turn.span, { event: { id: eventId, - input, + ...(input !== undefined ? { input } : {}), metadata, }, name: "eve.step", @@ -301,14 +342,14 @@ class EveBridge { spanId, startTime: eventTime(event), }); - span.log({ input, metadata }); + span.log({ ...(input !== undefined ? { input } : {}), metadata }); turn.stepsByIndex.set(event.data.stepIndex, { + ...(input !== undefined ? { input } : {}), metadata, metrics: {}, ordinal: stepOrdinal, span, - toolCalls: [], }); } @@ -321,6 +362,13 @@ class EveBridge { return; } + const existingMessage = + Array.isArray(step.output) && isObject(step.output[0]) + ? step.output[0].message + : undefined; + const existingToolCalls = isObject(existingMessage) + ? existingMessage.tool_calls + : undefined; step.output = [ { finish_reason: normalizedFinishReason(event.data.finishReason), @@ -328,24 +376,16 @@ class EveBridge { message: { content: event.data.message, role: "assistant", - ...(step.toolCalls.length > 0 - ? { tool_calls: [...step.toolCalls] } + ...(Array.isArray(existingToolCalls) + ? { tool_calls: existingToolCalls } : {}), }, }, ]; - if (step.assistantToolCallMessage) { - step.assistantToolCallMessage.content = event.data.message; - step.assistantToolCallMessage.tool_calls = [...step.toolCalls]; - } const turn = this.turnForEvent(event, ctx); if (turn && event.data.finishReason !== "tool-calls") { turn.output = event.data.message; - turn.messages.push({ - content: event.data.message, - role: "assistant", - }); } } @@ -391,13 +431,6 @@ class EveBridge { } for (const action of traceActions) { - if (step) { - const toolCall = toolCallMessageFromAction(action); - if (!step.toolCalls.some((existing) => existing.id === toolCall.id)) { - step.toolCalls.push(toolCall); - } - } - if (isToolCallAction(action)) { await this.startRequestedTool(event, turn, sessionId, action, step); } else if (isLocalSubagentCallAction(action)) { @@ -406,51 +439,34 @@ class EveBridge { } if (!step) { - turn.messages.push({ - content: null, - role: "assistant", - tool_calls: traceActions.map(toolCallMessageFromAction), - }); return; } - const outputChoice = - Array.isArray(step.output) && isObject(step.output[0]) - ? step.output[0] - : undefined; - const outputMessage = outputChoice - ? Reflect.get(outputChoice, "message") - : undefined; - const content = isObject(outputMessage) - ? Reflect.get(outputMessage, "content") - : null; - if (!step.assistantToolCallMessage) { - step.assistantToolCallMessage = { - content, - role: "assistant", - tool_calls: [...step.toolCalls], - }; - turn.messages.push(step.assistantToolCallMessage); - } else { - step.assistantToolCallMessage.content = content; - step.assistantToolCallMessage.tool_calls = [...step.toolCalls]; - } - - if (isObject(outputMessage)) { - Reflect.set(outputMessage, "tool_calls", [...step.toolCalls]); - } else { - step.output = [ - { - finish_reason: "tool_calls", - index: 0, - message: { - content, - role: "assistant", - tool_calls: [...step.toolCalls], - }, + step.output = [ + { + finish_reason: "tool_calls", + index: 0, + message: { + content: null, + role: "assistant", + tool_calls: traceActions.map((action) => { + const name = + action.kind === "tool-call" + ? action.toolName + : (action.subagentName ?? action.name ?? "agent"); + return { + function: { + arguments: JSON.stringify(action.input), + name, + }, + id: action.callId, + type: "function", + }; + }), }, - ]; - } + }, + ]; + step.span.log({ metadata: step.metadata, output: step.output }); } private async handleActionResult( @@ -509,14 +525,6 @@ class EveBridge { output: result.output, }); - const turn = this.turnForEvent(event, ctx); - turn?.messages.push({ - content: toolMessageContent(result.output), - name: result.toolName, - role: "tool", - tool_call_id: result.callId, - }); - const endTime = eventTime(event); tool.span.end(endTime === undefined ? undefined : { endTime }); this.toolsByCallKey.delete(key); @@ -542,18 +550,29 @@ class EveBridge { } const key = toolKey(sessionId, event.data.callId); - const metadata = { ...turn.metadata }; + const metadata = toolMetadataFromTurn(turn); const existing = this.toolsByCallKey.get(key); if (existing) { existing.metadata = { ...existing.metadata, ...metadata }; existing.span.log({ metadata: existing.metadata }); return; } + if (this.completedToolKeys.has(key)) { + return; + } const [eventId, spanId] = await Promise.all([ rowIdForSubagent(sessionId, event.data.callId), spanIdForSubagent(sessionId, event.data.callId), ]); + const pending = this.toolsByCallKey.get(key); + if (pending || this.completedToolKeys.has(key)) { + if (pending) { + pending.metadata = { ...pending.metadata, ...metadata }; + pending.span.log({ metadata: pending.metadata }); + } + return; + } const span = startChildSpan(turn.span, { event: { id: eventId, @@ -604,14 +623,6 @@ class EveBridge { status: event.data.status, }); - const turn = this.turnForEvent(event, ctx); - turn?.messages.push({ - content: toolMessageContent(event.data.output), - name: event.data.subagentName, - role: "tool", - tool_call_id: event.data.callId, - }); - this.toolsByCallKey.delete(key); this.completedToolKeys.add(key); if (flushAfterCompletion) { @@ -659,14 +670,6 @@ class EveBridge { status: event.data.status, }); - const turn = this.turnForEvent(event, ctx); - turn?.messages.push({ - content: toolMessageContent(result.output), - name: result.subagentName, - role: "tool", - tool_call_id: result.callId, - }); - this.toolsByCallKey.delete(key); this.completedToolKeys.add(key); if (flushAfterCompletion) { @@ -734,16 +737,15 @@ class EveBridge { }; step.metrics = { ...step.metrics, ...metrics }; if (Array.isArray(step.output) && isObject(step.output[0])) { - const finishReason = Reflect.get(step.output[0], "finish_reason"); + const finishReason = step.output[0].finish_reason; if (typeof finishReason !== "string") { - Reflect.set( - step.output[0], - "finish_reason", - normalizedFinishReason(event.data.finishReason), + step.output[0].finish_reason = normalizedFinishReason( + event.data.finishReason, ); } } step.span.log({ + ...(step.input !== undefined ? { input: step.input } : {}), metadata: step.metadata, metrics, output: step.output, @@ -855,8 +857,11 @@ class EveBridge { for (const [key, tool] of this.toolsByCallKey) { if (key.startsWith(`${sessionId}:`)) { const endTime = eventTime(event); - tool.span.end(endTime === undefined ? undefined : { endTime }); - this.toolsByCallKey.delete(key); + if (!tool.endedByTurn) { + tool.span.log({ metadata: tool.metadata }); + tool.span.end(endTime === undefined ? undefined : { endTime }); + tool.endedByTurn = true; + } } } @@ -865,21 +870,41 @@ class EveBridge { await this.flushInstrumentation(); } - private handleSessionCompleted(ctx: unknown): void { + private async handleSessionCompleted( + event: Extract, + ctx: unknown, + ): Promise { const sessionId = sessionIdFromContext(ctx); - if (sessionId) { - this.sessionsById.delete(sessionId); - for (const key of this.toolsByCallKey.keys()) { - if (key.startsWith(`${sessionId}:`)) { - this.toolsByCallKey.delete(key); - } + if (!sessionId) { + return; + } + + for (const [key, turn] of this.turnsByKey) { + if (!key.startsWith(`${sessionId}:`)) { + continue; } - for (const completedKey of this.completedToolKeys) { - if (completedKey.startsWith(`${sessionId}:`)) { - this.completedToolKeys.delete(completedKey); - } + this.endOpenChildrenForTurn(turn, eventTime(event)); + turn.span.log({ + metadata: turn.metadata, + metrics: turn.metrics, + output: turn.output, + }); + const endTime = eventTime(event); + turn.span.end(endTime === undefined ? undefined : { endTime }); + this.turnsByKey.delete(key); + } + + for (const [key, tool] of this.toolsByCallKey) { + if (key.startsWith(`${sessionId}:`) && !tool.endedByTurn) { + const endTime = eventTime(event); + tool.span.log({ metadata: tool.metadata }); + tool.span.end(endTime === undefined ? undefined : { endTime }); + tool.endedByTurn = true; } } + this.sessionsById.delete(sessionId); + + await this.flushInstrumentation(); } private async ensureTurn( @@ -903,13 +928,14 @@ class EveBridge { return existing; } - const metadata = { ...(hookMetadata ?? {}) }; + const metadata = { + ...(hookMetadata ?? {}), + ...(this.sessionsById.get(sessionId)?.metadata ?? {}), + }; const span = await this.startTurnSpan(sessionId, event, ctx, metadata); span.log({ metadata }); const state = { - inputLogged: false, key, - messages: [], metadata, metrics: {}, nextStepOrdinal: 0, @@ -928,15 +954,18 @@ class EveBridge { step?: StepState, ): Promise { const key = toolKey(sessionId, action.callId); - if (this.toolsByCallKey.has(key)) { + if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { return; } - const metadata = { ...turn.metadata }; + const metadata = toolMetadataFromTurn(turn); const [eventId, spanId] = await Promise.all([ rowIdForTool(sessionId, event.data.turnId, action.callId), spanIdForTool(sessionId, event.data.turnId, action.callId), ]); + if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { + return; + } const span = startChildSpan(turn.span, { event: { id: eventId, @@ -967,16 +996,19 @@ class EveBridge { step?: StepState, ): Promise { const key = toolKey(sessionId, action.callId); - if (this.toolsByCallKey.has(key)) { + if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { return; } const name = action.subagentName ?? action.name ?? "agent"; - const metadata = { ...turn.metadata }; + const metadata = toolMetadataFromTurn(turn); const [eventId, spanId] = await Promise.all([ rowIdForSubagent(sessionId, action.callId), spanIdForSubagent(sessionId, action.callId), ]); + if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { + return; + } const span = startChildSpan(turn.span, { event: { id: eventId, @@ -1011,11 +1043,15 @@ class EveBridge { return undefined; } - const metadata = { ...turn.metadata }; + const metadata = toolMetadataFromTurn(turn); const [eventId, spanId] = await Promise.all([ rowIdForTool(sessionId, event.data.turnId, result.callId), spanIdForTool(sessionId, event.data.turnId, result.callId), ]); + const existing = this.toolsByCallKey.get(toolKey(sessionId, result.callId)); + if (existing) { + return existing; + } const span = startChildSpan(turn.span, { event: { id: eventId, @@ -1049,11 +1085,17 @@ class EveBridge { return undefined; } - const metadata = { ...turn.metadata }; + const metadata = toolMetadataFromTurn(turn); const [eventId, spanId] = await Promise.all([ rowIdForSubagent(sessionId, event.data.callId), spanIdForSubagent(sessionId, event.data.callId), ]); + const existing = this.toolsByCallKey.get( + toolKey(sessionId, event.data.callId), + ); + if (existing) { + return existing; + } const span = startChildSpan(turn.span, { event: { id: eventId, @@ -1087,11 +1129,15 @@ class EveBridge { return undefined; } - const metadata = { ...turn.metadata }; + const metadata = toolMetadataFromTurn(turn); const [eventId, spanId] = await Promise.all([ rowIdForSubagent(sessionId, result.callId), spanIdForSubagent(sessionId, result.callId), ]); + const existing = this.toolsByCallKey.get(toolKey(sessionId, result.callId)); + if (existing) { + return existing; + } const span = startChildSpan(turn.span, { event: { id: eventId, @@ -1208,6 +1254,12 @@ class EveBridge { spanIdForTurn(lineage.sessionId, lineage.turnId), rootSpanIdForLineage(lineage), ]); + const pending = this.toolsByCallKey.get( + toolKey(lineage.sessionId, lineage.callId), + ); + if (pending) { + return; + } const parentTurn = this.turnsByKey.get( turnKey(lineage.sessionId, lineage.turnId), ); @@ -1262,6 +1314,7 @@ class EveBridge { private endOpenChildrenForTurn(turn: TurnState, endTime: number | undefined) { for (const step of turn.stepsByIndex.values()) { step.span.log({ + ...(step.input !== undefined ? { input: step.input } : {}), metadata: step.metadata, metrics: step.metrics, output: step.output, @@ -1274,6 +1327,9 @@ class EveBridge { if (tool.turnKey !== turn.key) { continue; } + if (tool.endedByTurn) { + continue; + } tool.span.log({ metadata: tool.metadata }); tool.span.end(endTime === undefined ? undefined : { endTime }); tool.endedByTurn = true; @@ -1308,19 +1364,251 @@ function subagentNameFromContext(ctx: unknown): string | undefined { if (!isObject(ctx)) { return undefined; } - const agent = Reflect.get(ctx, "agent"); + const agent = ctx["agent"]; if (!isObject(agent)) { return undefined; } - const name = Reflect.get(agent, "name"); + const name = agent["name"]; return typeof name === "string" ? name : undefined; } +function captureEveModelInput( + input: EveInstrumentationStepStartedEventInput, +): void { + if (!isObject(input)) { + return; + } + const session = input["session"]; + const turn = input["turn"]; + const step = input["step"]; + if (!isObject(session) || !isObject(turn) || !isObject(step)) { + return; + } + + const sessionId = session["id"]; + const turnId = turn["id"]; + const stepIndex = step["index"]; + if ( + typeof sessionId !== "string" || + typeof turnId !== "string" || + typeof stepIndex !== "number" || + !Number.isInteger(stepIndex) + ) { + return; + } + + const captured = capturedModelInput(input["modelInput"]); + if (!captured) { + return; + } + + const state = readEveLlmInputState(); + if (!state) { + return; + } + + const key = llmInputKey(sessionId, turnId, stepIndex); + const entries = state.entries.filter((entry) => entry.key !== key); + entries.push({ input: captured, key }); + writeEveLlmInputState({ + entries: entries.slice(-MAX_STORED_LLM_INPUTS), + }); +} + +function consumeCapturedEveModelInput( + sessionId: string, + turnId: string, + stepIndex: number, +): CapturedEveModelInput | undefined { + try { + const state = readEveLlmInputState(); + if (!state) { + return undefined; + } + + const key = llmInputKey(sessionId, turnId, stepIndex); + const entry = state.entries.find((candidate) => candidate.key === key); + if (!entry) { + return undefined; + } + + writeEveLlmInputState({ + entries: state.entries.filter((candidate) => candidate.key !== key), + }); + return entry.input; + } catch (error) { + debugLogger.warn("Error in Eve LLM input consumption:", error); + return undefined; + } +} + +function capturedModelInput( + modelInput: unknown, +): CapturedEveModelInput | undefined { + if (!isObject(modelInput)) { + return undefined; + } + + const messages = modelInput["messages"]; + if (!Array.isArray(messages)) { + return undefined; + } + + const instructions = modelInput["instructions"]; + const value = { + ...(instructions !== undefined ? { instructions } : {}), + messages, + }; + try { + const cloned: unknown = JSON.parse(JSON.stringify(value)); + if (!isObject(cloned)) { + return undefined; + } + const clonedMessages = cloned["messages"]; + if (!Array.isArray(clonedMessages)) { + return undefined; + } + const clonedInstructions = cloned["instructions"]; + if ( + clonedInstructions !== undefined && + typeof clonedInstructions !== "string" && + !Array.isArray(clonedInstructions) + ) { + return undefined; + } + return { + ...(clonedInstructions !== undefined + ? { instructions: clonedInstructions } + : {}), + messages: clonedMessages, + }; + } catch { + return undefined; + } +} + +function readEveLlmInputState(): EveLlmInputState | undefined { + const context = activeEveContext(); + const key = eveLlmInputStateKey(); + if (!context || !key) { + return undefined; + } + + const get = context["get"]; + if (typeof get !== "function") { + return undefined; + } + + let state: unknown; + try { + state = get.call(context, key); + } catch { + return undefined; + } + if (!isObject(state)) { + return { entries: [] }; + } + + const entries = state["entries"]; + if (!Array.isArray(entries)) { + return { entries: [] }; + } + + return { + entries: entries.flatMap((entry): EveLlmInputState["entries"] => { + if (!isObject(entry)) { + return []; + } + const entryKey = entry["key"]; + const entryInput = entry["input"]; + if (typeof entryKey !== "string" || !isCapturedModelInput(entryInput)) { + return []; + } + return [{ input: entryInput, key: entryKey }]; + }), + }; +} + +function writeEveLlmInputState(state: EveLlmInputState): void { + const context = activeEveContext(); + const key = eveLlmInputStateKey(); + if (!context || !key) { + return; + } + + const set = context["set"]; + if (typeof set !== "function") { + return; + } + + try { + set.call(context, key, state); + } catch { + return; + } +} + +function activeEveContext(): Record | undefined { + const storage = Object.getOwnPropertyDescriptor( + globalThis, + EVE_CONTEXT_STORAGE, + )?.value; + if (!isObject(storage)) { + return undefined; + } + + const getStore = storage["getStore"]; + if (typeof getStore !== "function") { + return undefined; + } + + const context = getStore.call(storage); + return isObject(context) ? context : undefined; +} + +function eveLlmInputStateKey(): object | undefined { + const registry = Object.getOwnPropertyDescriptor( + globalThis, + EVE_CONTEXT_KEY_REGISTRY, + )?.value; + if (!(registry instanceof Map)) { + return undefined; + } + + const existing = registry.get(EVE_LLM_INPUT_STATE_KEY); + if (isObject(existing)) { + return existing; + } + + registry.set(EVE_LLM_INPUT_STATE_KEY, EVE_LLM_INPUT_STATE); + return EVE_LLM_INPUT_STATE; +} + +function isCapturedModelInput(input: unknown): input is CapturedEveModelInput { + if (!isObject(input) || !Array.isArray(input["messages"])) { + return false; + } + const instructions = input["instructions"]; + return ( + instructions === undefined || + typeof instructions === "string" || + Array.isArray(instructions) + ); +} + +function llmInputKey( + sessionId: string, + turnId: string, + stepIndex: number, +): string { + return `${sessionId}\0${turnId}\0${stepIndex}`; +} + function modelMetadataFromRuntime(runtime: unknown): Record { if (!isObject(runtime)) { return {}; } - const modelId = Reflect.get(runtime, "modelId"); + const modelId = runtime["modelId"]; return typeof modelId === "string" ? modelMetadataFromModelId(modelId) : {}; } @@ -1347,30 +1635,45 @@ function sessionIdFromContext(ctx: unknown): string | undefined { if (!isObject(ctx)) { return undefined; } - const session = Reflect.get(ctx, "session"); + const session = ctx["session"]; if (!isObject(session)) { return undefined; } - const id = Reflect.get(session, "id"); + const id = session["id"]; return typeof id === "string" ? id : undefined; } +function queueKeyForEvent(event: Record, ctx: unknown) { + if (isObject(event.data)) { + const sessionId = event.data["sessionId"]; + if (typeof sessionId === "string") { + return sessionId; + } + } + return sessionIdFromContext(ctx) ?? "__unknown__"; +} + +function toolMetadataFromTurn(turn: TurnState): Record { + const { model: _model, provider: _provider, ...metadata } = turn.metadata; + return metadata; +} + function parentLineageFromContext(ctx: unknown): ParentLineage | undefined { if (!isObject(ctx)) { return undefined; } - const session = Reflect.get(ctx, "session"); + const session = ctx.session; if (!isObject(session)) { return undefined; } - const parent = Reflect.get(session, "parent"); + const parent = session["parent"]; if (!isObject(parent)) { return undefined; } - const callId = Reflect.get(parent, "callId"); - const rootSessionId = Reflect.get(parent, "rootSessionId"); - const sessionId = Reflect.get(parent, "sessionId"); + const callId = parent["callId"]; + const rootSessionId = parent["rootSessionId"]; + const sessionId = parent["sessionId"]; if ( typeof callId !== "string" || typeof rootSessionId !== "string" || @@ -1379,11 +1682,9 @@ function parentLineageFromContext(ctx: unknown): ParentLineage | undefined { return undefined; } - const turn = Reflect.get(parent, "turn"); - const turnId = isObject(turn) ? Reflect.get(turn, "id") : undefined; - const turnSequence = isObject(turn) - ? Reflect.get(turn, "sequence") - : undefined; + const turn = parent["turn"]; + const turnId = isObject(turn) ? turn["id"] : undefined; + const turnSequence = isObject(turn) ? turn["sequence"] : undefined; return { callId, rootSessionId, @@ -1398,10 +1699,10 @@ function isToolCallAction( ): action is EveRuntimeToolCallActionRequest { return ( isObject(action) && - Reflect.get(action, "kind") === "tool-call" && - typeof Reflect.get(action, "callId") === "string" && - typeof Reflect.get(action, "toolName") === "string" && - isObject(Reflect.get(action, "input")) + action["kind"] === "tool-call" && + typeof action["callId"] === "string" && + typeof action["toolName"] === "string" && + isObject(action["input"]) ); } @@ -1410,9 +1711,9 @@ function isLocalSubagentCallAction( ): action is Extract { return ( isObject(action) && - Reflect.get(action, "kind") === "subagent-call" && - typeof Reflect.get(action, "callId") === "string" && - isObject(Reflect.get(action, "input")) + action["kind"] === "subagent-call" && + typeof action["callId"] === "string" && + isObject(action["input"]) ); } @@ -1429,9 +1730,9 @@ function isToolResult( ): result is EveRuntimeToolResultActionResult { return ( isObject(result) && - Reflect.get(result, "kind") === "tool-result" && - typeof Reflect.get(result, "callId") === "string" && - typeof Reflect.get(result, "toolName") === "string" + result["kind"] === "tool-result" && + typeof result["callId"] === "string" && + typeof result["toolName"] === "string" ); } @@ -1440,46 +1741,12 @@ function isSubagentResult( ): result is Extract { return ( isObject(result) && - Reflect.get(result, "kind") === "subagent-result" && - typeof Reflect.get(result, "callId") === "string" && - typeof Reflect.get(result, "subagentName") === "string" + result["kind"] === "subagent-result" && + typeof result["callId"] === "string" && + typeof result["subagentName"] === "string" ); } -function toolCallMessageFromAction( - action: - | EveRuntimeToolCallActionRequest - | Extract, -): ToolCall { - const name = - action.kind === "tool-call" - ? action.toolName - : (action.subagentName ?? action.name ?? "agent"); - return { - function: { - arguments: JSON.stringify(action.input), - name, - }, - id: action.callId, - type: "function", - }; -} - -function toolMessageContent(output: unknown): string { - if (typeof output === "string") { - return output; - } - try { - const serialized = JSON.stringify(output); - if (typeof serialized === "string") { - return serialized; - } - } catch { - // Fall back to a string representation for unexpected hook payloads. - } - return output === undefined || output === null ? "" : String(output); -} - function normalizedFinishReason( finishReason: EveAssistantStepFinishReason, ): string { diff --git a/js/src/vendor-sdk-types/eve.ts b/js/src/vendor-sdk-types/eve.ts index d4f0699db..025e80360 100644 --- a/js/src/vendor-sdk-types/eve.ts +++ b/js/src/vendor-sdk-types/eve.ts @@ -305,3 +305,43 @@ export interface EveHookDefinition { | undefined; }; } + +export interface EveInstrumentationSetupContext { + readonly agentName: string; +} + +export interface EveInstrumentationModelInput { + readonly instructions?: string | readonly EveJsonObject[]; + readonly messages: readonly EveJsonObject[]; +} + +export interface EveInstrumentationStepStartedEventInput { + readonly channel?: unknown; + readonly modelInput: EveInstrumentationModelInput; + readonly session: { + readonly id?: string; + }; + readonly step: { + readonly index?: number; + }; + readonly turn: { + readonly id?: string; + readonly sequence?: number; + }; +} + +export interface EveInstrumentationDefinition { + readonly events?: { + readonly "step.started"?: ( + input: EveInstrumentationStepStartedEventInput, + ) => void | { readonly runtimeContext?: EveJsonObject }; + readonly [eventType: string]: + | (( + input: EveInstrumentationStepStartedEventInput, + ) => void | { readonly runtimeContext?: EveJsonObject }) + | undefined; + }; + readonly recordInputs?: boolean; + readonly recordOutputs?: boolean; + readonly setup?: (context: EveInstrumentationSetupContext) => void; +} From 8617a1d8ac75d111f68af64911e279fa6c625d06 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 10 Jul 2026 11:55:26 +0200 Subject: [PATCH 07/10] system prompt as message instead of instruction --- .changeset/eve-instrumentation.md | 5 + .../eve-instrumentation.span-tree.json | 387 +++++++++--------- .../eve-instrumentation.span-tree.txt | 387 +++++++++--------- .../eve-instrumentation/scenario.test.ts | 16 +- .../plugins/eve-plugin.test.ts | 15 +- js/src/instrumentation/plugins/eve-plugin.ts | 46 +-- 6 files changed, 429 insertions(+), 427 deletions(-) create mode 100644 .changeset/eve-instrumentation.md diff --git a/.changeset/eve-instrumentation.md b/.changeset/eve-instrumentation.md new file mode 100644 index 000000000..c0e75a08a --- /dev/null +++ b/.changeset/eve-instrumentation.md @@ -0,0 +1,5 @@ +--- +"braintrust": patch +--- + +feat: Add Eve instrumentation helpers for Braintrust tracing diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json index 78cfb22ed..8f065a81c 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -8,15 +8,16 @@ "name": "eve.step", "type": "llm", "children": [], - "input": { - "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "messages": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ] - }, + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], "output": [ { "finish_reason": "tool_calls", @@ -62,15 +63,16 @@ "name": "eve.step", "type": "llm", "children": [], - "input": { - "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "messages": [ - { - "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ] - }, + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], "output": [ { "finish_reason": "tool_calls", @@ -125,56 +127,57 @@ "name": "eve.step", "type": "llm", "children": [], - "input": { - "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "messages": [ - { - "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": [ - { - "input": { - "query": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "search", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "json", - "value": { - "query": "Run the Braintrust Eve instrumentation e2e scenario", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - } - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "search", - "type": "tool-result" - } - ], - "role": "tool" - } - ] - }, + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "query": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-result" + } + ], + "role": "tool" + } + ], "output": [ { "finish_reason": "stop", @@ -233,47 +236,48 @@ "name": "eve.step", "type": "llm", "children": [], - "input": { - "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "messages": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": [ - { - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "text", - "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-result" - } - ], - "role": "tool" - } - ] - }, + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + } + ], "output": [ { "finish_reason": "tool_calls", @@ -329,89 +333,90 @@ "name": "eve.step", "type": "llm", "children": [], - "input": { - "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "messages": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": [ - { - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "text", - "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-result" - } - ], - "role": "tool" - }, - { - "content": [ - { - "input": { + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", "url": "https://eve.dev/docs/guides/instrumentation" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "json", - "value": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - "section": "Runtime context", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - } - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-result" - } - ], - "role": "tool" - } - ] - }, + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + } + ], "output": [ { "finish_reason": "stop", diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt index 7dd3062e3..f82c3aea3 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -20,15 +20,16 @@ span_tree: "tokens": 20317 } ├── eve.step [llm] - │ input: { - │ "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ "messages": [ - │ { - │ "content": "Run the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ } - │ ] - │ } + │ input: [ + │ { + │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "role": "system" + │ }, + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ } + │ ] │ output: [ │ { │ "finish_reason": "tool_calls", @@ -91,15 +92,16 @@ span_tree: │ "tokens": 13226 │ } │ ├── eve.step [llm] - │ │ input: { - │ │ "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ │ "messages": [ - │ │ { - │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - │ │ "role": "user" - │ │ } - │ │ ] - │ │ } + │ │ input: [ + │ │ { + │ │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ │ "role": "system" + │ │ }, + │ │ { + │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ │ "role": "user" + │ │ } + │ │ ] │ │ output: [ │ │ { │ │ "finish_reason": "tool_calls", @@ -146,56 +148,57 @@ span_tree: │ │ "testRunId": "" │ │ } │ └── eve.step [llm] - │ input: { - │ "instructions": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ "messages": [ - │ { - │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ }, - │ { - │ "content": [ - │ { - │ "input": { - │ "query": "Run the Braintrust Eve instrumentation e2e scenario" - │ }, - │ "providerOptions": { - │ "openai": { - │ "itemId": "" - │ } - │ }, - │ "toolCallId": "", - │ "toolName": "search", - │ "type": "tool-call" - │ } - │ ], - │ "role": "assistant" - │ }, - │ { - │ "content": [ - │ { - │ "output": { - │ "type": "json", - │ "value": { - │ "query": "Run the Braintrust Eve instrumentation e2e scenario", - │ "title": "Eve instrumentation", - │ "url": "https://eve.dev/docs/guides/instrumentation" - │ } - │ }, - │ "providerOptions": { - │ "openai": { - │ "itemId": "" - │ } - │ }, - │ "toolCallId": "", - │ "toolName": "search", - │ "type": "tool-result" - │ } - │ ], - │ "role": "tool" - │ } - │ ] - │ } + │ input: [ + │ { + │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "role": "system" + │ }, + │ { + │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "search", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "json", + │ "value": { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "search", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ } + │ ] │ output: [ │ { │ "finish_reason": "stop", @@ -219,47 +222,48 @@ span_tree: │ "tokens": 6646 │ } ├── eve.step [llm] - │ input: { - │ "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ "messages": [ - │ { - │ "content": "Run the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ }, - │ { - │ "content": [ - │ { - │ "input": { - │ "message": "Run the Braintrust Eve instrumentation e2e scenario" - │ }, - │ "providerOptions": { - │ "openai": { - │ "itemId": "" - │ } - │ }, - │ "toolCallId": "", - │ "toolName": "researcher", - │ "type": "tool-call" - │ } - │ ], - │ "role": "assistant" - │ }, - │ { - │ "content": [ - │ { - │ "output": { - │ "type": "text", - │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - │ }, - │ "toolCallId": "", - │ "toolName": "researcher", - │ "type": "tool-result" - │ } - │ ], - │ "role": "tool" - │ } - │ ] - │ } + │ input: [ + │ { + │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "role": "system" + │ }, + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "text", + │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ } + │ ] │ output: [ │ { │ "finish_reason": "tool_calls", @@ -307,89 +311,90 @@ span_tree: │ "testRunId": "" │ } └── eve.step [llm] - input: { - "instructions": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "messages": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": [ - { - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "text", - "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-result" - } - ], - "role": "tool" - }, - { - "content": [ - { - "input": { + input: [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", "url": "https://eve.dev/docs/guides/instrumentation" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "json", - "value": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - "section": "Runtime context", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - } - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-result" - } - ], - "role": "tool" - } - ] - } + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + } + ] output: [ { "finish_reason": "stop", diff --git a/e2e/scenarios/eve-instrumentation/scenario.test.ts b/e2e/scenarios/eve-instrumentation/scenario.test.ts index b7737f335..25e38d332 100644 --- a/e2e/scenarios/eve-instrumentation/scenario.test.ts +++ b/e2e/scenarios/eve-instrumentation/scenario.test.ts @@ -79,9 +79,11 @@ describe("eve instrumentation", () => { expect(steps.map((step) => step.span.type)).toEqual(["llm", "llm", "llm"]); for (const step of steps) { expect(step.span.parentIds).toEqual([root?.span.id]); - expect(step.input).toMatchObject({ - messages: expect.any(Array), - }); + expect(Array.isArray(step.input)).toBe(true); + if (!Array.isArray(step.input)) { + throw new Error("Expected Eve step input to be a message array"); + } + expect(step.input[0]).toMatchObject({ role: "system" }); expect(step.metadata).toMatchObject({ model: "gpt-5.4-mini", provider: "openai", @@ -115,9 +117,11 @@ describe("eve instrumentation", () => { for (const step of childSteps) { expect(step.span.type).toBe("llm"); expect(step.span.parentIds).toEqual([childTurn?.span.id]); - expect(step.input).toMatchObject({ - messages: expect.any(Array), - }); + expect(Array.isArray(step.input)).toBe(true); + if (!Array.isArray(step.input)) { + throw new Error("Expected Eve step input to be a message array"); + } + expect(step.input[0]).toMatchObject({ role: "system" }); expect(step.metadata).toMatchObject({ model: "gpt-5.4-mini", provider: "openai", diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index cbd2dcac2..3a12ef726 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -208,7 +208,16 @@ describe("braintrustEveHook", () => { const step = spans.find( (span) => span.span_attributes?.name === "eve.step", ); - expect(step?.input).toEqual(modelInput); + expect(step?.input).toEqual([ + { + content: "Answer with the relevant Eve instrumentation detail.", + role: "system", + }, + { + content: "What should Braintrust capture?", + role: "user", + }, + ]); expect(fakeEve.values.get(EVE_LLM_INPUT_STATE_KEY)).toEqual({ entries: [], }); @@ -223,7 +232,7 @@ describe("braintrustEveHook", () => { const fakeEve = installFakeEveContext(); restoreFakeEveContext = fakeEve.restore; fakeEve.values.set(EVE_LLM_INPUT_STATE_KEY, { - entries: [{ input: { messages: "not an array" }, key: "bad" }], + entries: [{ input: { content: "not an array" }, key: "bad" }], }); expect(() => instrumentation.events?.["step.started"]?.({ @@ -1230,7 +1239,7 @@ describe("braintrustEveHook", () => { /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, ); expect(parentTurn?.root_span_id).toMatch( - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, + /^([0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/, ); expect(subagentSpans[0]?.span_parents).toEqual([parentTurn?.span_id]); expect(childTurn?.span_parents).toEqual([subagentSpans[0]?.span_id]); diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index 5d6428028..19b4d9cf7 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -59,10 +59,7 @@ const EVE_LLM_INPUT_STATE_KEY = "braintrust.eve.llmInputs"; const EVE_LLM_INPUT_STATE = { name: EVE_LLM_INPUT_STATE_KEY }; const MAX_STORED_LLM_INPUTS = 100; -type CapturedEveModelInput = { - instructions?: string | readonly unknown[]; - messages: readonly unknown[]; -}; +type CapturedEveModelInput = readonly unknown[]; type EveLlmInputState = { entries: readonly { @@ -1455,33 +1452,18 @@ function capturedModelInput( } const instructions = modelInput["instructions"]; - const value = { - ...(instructions !== undefined ? { instructions } : {}), - messages, - }; + const value = [ + ...(instructions !== undefined + ? [{ content: instructions, role: "system" }] + : []), + ...messages, + ]; try { const cloned: unknown = JSON.parse(JSON.stringify(value)); - if (!isObject(cloned)) { - return undefined; - } - const clonedMessages = cloned["messages"]; - if (!Array.isArray(clonedMessages)) { - return undefined; - } - const clonedInstructions = cloned["instructions"]; - if ( - clonedInstructions !== undefined && - typeof clonedInstructions !== "string" && - !Array.isArray(clonedInstructions) - ) { + if (!Array.isArray(cloned)) { return undefined; } - return { - ...(clonedInstructions !== undefined - ? { instructions: clonedInstructions } - : {}), - messages: clonedMessages, - }; + return cloned; } catch { return undefined; } @@ -1585,15 +1567,7 @@ function eveLlmInputStateKey(): object | undefined { } function isCapturedModelInput(input: unknown): input is CapturedEveModelInput { - if (!isObject(input) || !Array.isArray(input["messages"])) { - return false; - } - const instructions = input["instructions"]; - return ( - instructions === undefined || - typeof instructions === "string" || - Array.isArray(instructions) - ); + return Array.isArray(input); } function llmInputKey( From 53d75aafe7492ae27586dd3a7f38f6258b830124 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 10 Jul 2026 13:33:23 +0200 Subject: [PATCH 08/10] reuse session as trace root --- .../__cassettes__/eve-v0-20-0.cassette.json | 2627 +++++++++++++++-- .../eve-instrumentation.span-tree.json | 1472 ++++++--- .../eve-instrumentation.span-tree.txt | 1458 ++++++--- .../eve-instrumentation/scenario.test.ts | 93 +- e2e/scenarios/eve-instrumentation/scenario.ts | 62 +- .../plugins/eve-plugin.test.ts | 117 +- js/src/instrumentation/plugins/eve-plugin.ts | 213 +- 7 files changed, 5046 insertions(+), 996 deletions(-) diff --git a/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json b/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json index e39ea21af..31b22e3ee 100644 --- a/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json +++ b/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json @@ -4,7 +4,7 @@ "callIndex": 0, "id": "b18dba30bbb5cd34", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-09T11:20:16.531Z", + "recordedAt": "2026-07-10T11:11:16.190Z", "request": { "body": { "kind": "json", @@ -375,26 +375,26 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83ef76a081a188d35d3e91cf3b1d\",\"object\":\"response\",\"created_at\":1783596015,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83ef76a081a188d35d3e91cf3b1d\",\"object\":\"response\",\"created_at\":1783596015,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_UiWXOc9U8IcpmtTc8pwI68SC\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"r06Lt7wN2uigNU\",\"output_index\":0,\"sequence_number\":3}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"message\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"kfp6duqAO\",\"output_index\":0,\"sequence_number\":4}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"DJeTPH0SmOA30\",\"output_index\":0,\"sequence_number\":5}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"xDPyBL4HXu3rH\",\"output_index\":0,\"sequence_number\":6}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"XpzaRbFEXP2l\",\"output_index\":0,\"sequence_number\":7}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"x1OoIccaee\",\"output_index\":0,\"sequence_number\":8}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"LKBIzXmUY4g\",\"output_index\":0,\"sequence_number\":9}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"YnbcwnaVRE2d\",\"output_index\":0,\"sequence_number\":10}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"AzuidOp6Vzi5ti\",\"output_index\":0,\"sequence_number\":12}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"Y1rEv3yfvbiIiu8\",\"output_index\":0,\"sequence_number\":13}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"6oF5AEuOnKTEaTR\",\"output_index\":0,\"sequence_number\":14}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"txOnYRU\",\"output_index\":0,\"sequence_number\":15}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"obfuscation\":\"0RK2NLvIilXvUf\",\"output_index\":0,\"sequence_number\":16}", - "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"output_index\":0,\"sequence_number\":17}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_UiWXOc9U8IcpmtTc8pwI68SC\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":18}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83ef76a081a188d35d3e91cf3b1d\",\"object\":\"response\",\"created_at\":1783596015,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596016,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_UiWXOc9U8IcpmtTc8pwI68SC\",\"name\":\"researcher\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6666,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":28,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6694},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3532300819eb6163603d2cf2712\",\"object\":\"response\",\"created_at\":1783681875,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3532300819eb6163603d2cf2712\",\"object\":\"response\",\"created_at\":1783681875,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_Pes0Oql8gdb9NEgsBRsmXe4y\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"BrS29XGOLn6lQZ\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"message\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"A8YYG0Fw5\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"KAeGo9soOEh7V\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"yoFqfCeCksZZg\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"QSAzHplRCrUz\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"aStgOqIZBa\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"HlWI4quIqtF\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"vjfsiKr7Dfj7\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"OuhRAjzrtWBrGp\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"c3dlDccMRXV2UZn\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"tm16A3vdlrhcHMP\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"3XWhfOV\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"obfuscation\":\"CY0gLmXO91StiF\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_Pes0Oql8gdb9NEgsBRsmXe4y\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":18}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3532300819eb6163603d2cf2712\",\"object\":\"response\",\"created_at\":1783681875,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681876,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_Pes0Oql8gdb9NEgsBRsmXe4y\",\"name\":\"researcher\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6666,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":28,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6694},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" ], "kind": "sse" }, @@ -402,11 +402,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a186f0337abf4baf-VIE", + "cf-ray": "a18f2066aa2d5b18-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Thu, 09 Jul 2026 11:20:15 GMT", - "openai-processing-ms": "413", + "date": "Fri, 10 Jul 2026 11:11:15 GMT", + "openai-processing-ms": "309", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -419,7 +419,7 @@ "x-ratelimit-remaining-tokens": "179992692", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "1e202202-8520-470e-aaca-0a68a2bd4d5a" + "x-request-id": "req_4b470a9abe484a2ba4200567b7e4472f" }, "status": 200, "statusText": "OK" @@ -429,7 +429,7 @@ "callIndex": 1, "id": "46596331f4831e14", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-09T11:20:17.751Z", + "recordedAt": "2026-07-10T11:11:18.151Z", "request": { "body": { "kind": "json", @@ -764,26 +764,26 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f0e54481a3b626579ce09833cd\",\"object\":\"response\",\"created_at\":1783596016,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f0e54481a3b626579ce09833cd\",\"object\":\"response\",\"created_at\":1783596016,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_bsPk0zM9ENF7o5XXNuEmMjhA\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"4vtnIIO7ut7BE8\",\"output_index\":0,\"sequence_number\":3}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"query\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"HwxIxM3Vnkw\",\"output_index\":0,\"sequence_number\":4}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"7ttA2p4MMqYUq\",\"output_index\":0,\"sequence_number\":5}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"aGkGxnSf3goW0\",\"output_index\":0,\"sequence_number\":6}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"jHLbTBVTHjQW\",\"output_index\":0,\"sequence_number\":7}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"efEEbcQutQ\",\"output_index\":0,\"sequence_number\":8}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"qCoSFZDB4aR\",\"output_index\":0,\"sequence_number\":9}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"nBI80OUUZxbm\",\"output_index\":0,\"sequence_number\":10}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"glsQVaBZXgtYGX\",\"output_index\":0,\"sequence_number\":12}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"VriyQtUYoi9mRp4\",\"output_index\":0,\"sequence_number\":13}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"EgYqpoUKz19UNAw\",\"output_index\":0,\"sequence_number\":14}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"Yz4DPVG\",\"output_index\":0,\"sequence_number\":15}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"obfuscation\":\"1OIerAy9poSd7e\",\"output_index\":0,\"sequence_number\":16}", - "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"output_index\":0,\"sequence_number\":17}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_bsPk0zM9ENF7o5XXNuEmMjhA\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":18}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f0e54481a3b626579ce09833cd\",\"object\":\"response\",\"created_at\":1783596016,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596017,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_bsPk0zM9ENF7o5XXNuEmMjhA\",\"name\":\"search\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6554,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":5504},\"output_tokens\":26,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6580},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b25bf43e95537d5006a50d354de7481a1a0622bdb46323b98\",\"object\":\"response\",\"created_at\":1783681876,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b25bf43e95537d5006a50d354de7481a1a0622bdb46323b98\",\"object\":\"response\",\"created_at\":1783681876,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_5jW377vXIipksfuxEtfn1zGJ\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"92L4eCM0HxeHJM\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"query\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"xkwAC81HmWr\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"0gDfRwx4UhUAb\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"TSt11GajCFSim\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"DmxleM8i8NMR\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"GUH9ruaVSw\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"ObOguRqCQIx\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"Wv0wN3Il9KNK\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"AkxCsRN3s3gzgV\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"ra5rmkqxbKlLqIK\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"x1tYfy4WeEaQ7OH\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"7cKinib\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"obfuscation\":\"zoAETmdOvKD5tA\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"item_id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_5jW377vXIipksfuxEtfn1zGJ\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":18}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b25bf43e95537d5006a50d354de7481a1a0622bdb46323b98\",\"object\":\"response\",\"created_at\":1783681876,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681878,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario\\\"}\",\"call_id\":\"call_5jW377vXIipksfuxEtfn1zGJ\",\"name\":\"search\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6554,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":26,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6580},\"user\":null,\"metadata\":{}},\"sequence_number\":19}" ], "kind": "sse" }, @@ -791,11 +791,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a186f040bc704baf-VIE", + "cf-ray": "a18f2071cc775b18-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Thu, 09 Jul 2026 11:20:17 GMT", - "openai-processing-ms": "330", + "date": "Fri, 10 Jul 2026 11:11:17 GMT", + "openai-processing-ms": "241", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -808,7 +808,7 @@ "x-ratelimit-remaining-tokens": "179992806", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "d8fbf635-d64b-4692-a655-8facd30110ef" + "x-request-id": "req_ebf26cc15d7449e29aac8ef627bcf657" }, "status": 200, "statusText": "OK" @@ -816,9 +816,9 @@ }, { "callIndex": 2, - "id": "7e6e893cf4f62b3b", + "id": "d1e7598a829dc70c", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-09T11:20:18.923Z", + "recordedAt": "2026-07-10T11:11:19.759Z", "request": { "body": { "kind": "json", @@ -839,11 +839,11 @@ "role": "user" }, { - "id": "fc_029cc1b39b8c4dde006a4f83f16a3881a386f4faad710c5b16", + "id": "fc_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f", "type": "item_reference" }, { - "call_id": "call_bsPk0zM9ENF7o5XXNuEmMjhA", + "call_id": "call_5jW377vXIipksfuxEtfn1zGJ", "output": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", "type": "function_call_output" } @@ -1162,32 +1162,32 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f2069c81a39c90caedbc0c9352\",\"object\":\"response\",\"created_at\":1783596018,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f2069c81a39c90caedbc0c9352\",\"object\":\"response\",\"created_at\":1783596018,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Research\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"JRipkwD1\",\"output_index\":0,\"sequence_number\":4}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"er\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"atTZhdzsScalqm\",\"output_index\":0,\"sequence_number\":5}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"zrRLUJyXe\",\"output_index\":0,\"sequence_number\":6}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"wmyhP0radRs3MKw\",\"output_index\":0,\"sequence_number\":7}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"yLjwf9XNRjnw\",\"output_index\":0,\"sequence_number\":8}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":9}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"KwwyoCmDJDcZA1\",\"output_index\":0,\"sequence_number\":10}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"JB6XrqKLc7\",\"output_index\":0,\"sequence_number\":11}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"ujU5csr5noh8x\",\"output_index\":0,\"sequence_number\":12}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"QgORFdpecykmJ\",\"output_index\":0,\"sequence_number\":13}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"eCwnNNzfBNUH\",\"output_index\":0,\"sequence_number\":14}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"JVqGWcybjOW\",\"output_index\":0,\"sequence_number\":15}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"OnWFi9JsgYqgiM\",\"output_index\":0,\"sequence_number\":16}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"j3olh3YhMHynn\",\"output_index\":0,\"sequence_number\":17}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"3ExmkOHn9MkYTt\",\"output_index\":0,\"sequence_number\":18}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"K2I9nTixsLfGb\",\"output_index\":0,\"sequence_number\":19}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"KMxU28uX\",\"output_index\":0,\"sequence_number\":20}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"obfuscation\":\"0GvouO2FICn\",\"output_index\":0,\"sequence_number\":21}", - "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":22,\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}", - "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"},\"sequence_number\":23}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":24}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_029cc1b39b8c4dde006a4f83f2069c81a39c90caedbc0c9352\",\"object\":\"response\",\"created_at\":1783596018,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596018,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_029cc1b39b8c4dde006a4f83f2800c81a3b4348b75e9cdc06d\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6624,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":22,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6646},\"user\":null,\"metadata\":{}},\"sequence_number\":25}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b25bf43e95537d5006a50d3568d4081a1aae1a8f1ce629f0b\",\"object\":\"response\",\"created_at\":1783681878,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b25bf43e95537d5006a50d3568d4081a1aae1a8f1ce629f0b\",\"object\":\"response\",\"created_at\":1783681878,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Research\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"2utkKIxE\",\"output_index\":0,\"sequence_number\":4}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"er\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"XLBrPVBSBLewHM\",\"output_index\":0,\"sequence_number\":5}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"X7i2sJV9b\",\"output_index\":0,\"sequence_number\":6}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"3WCMxbjcA8i1RVC\",\"output_index\":0,\"sequence_number\":7}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"x4LyTZlBkDLy\",\"output_index\":0,\"sequence_number\":8}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":9}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"XsimdEE4fkzHhe\",\"output_index\":0,\"sequence_number\":10}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"ZWwwfPYj7Q\",\"output_index\":0,\"sequence_number\":11}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"zBSBl21wEEZZY\",\"output_index\":0,\"sequence_number\":12}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"ILyy92R3TKOke\",\"output_index\":0,\"sequence_number\":13}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"IZJqd2OVOwn6\",\"output_index\":0,\"sequence_number\":14}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"Bc8a27VqjFD\",\"output_index\":0,\"sequence_number\":15}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"ENfPh6WYl4LNQc\",\"output_index\":0,\"sequence_number\":16}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"MYoOw8wq1WWdi\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"TWA7svsTTcxV3Z\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"qYEtIC2UKL1Q3\",\"output_index\":0,\"sequence_number\":19}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"saskRvnX\",\"output_index\":0,\"sequence_number\":20}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"obfuscation\":\"f1xyVKhWhNz\",\"output_index\":0,\"sequence_number\":21}", + "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":22,\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}", + "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"},\"sequence_number\":23}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":24}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b25bf43e95537d5006a50d3568d4081a1aae1a8f1ce629f0b\",\"object\":\"response\",\"created_at\":1783681878,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681879,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_0b25bf43e95537d5006a50d357819881a1b0ee299d0b05aefb\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6624,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":5504},\"output_tokens\":22,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6646},\"user\":null,\"metadata\":{}},\"sequence_number\":25}" ], "kind": "sse" }, @@ -1195,11 +1195,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a186f047da804baf-VIE", + "cf-ray": "a18f207c1eb75b18-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Thu, 09 Jul 2026 11:20:18 GMT", - "openai-processing-ms": "340", + "date": "Fri, 10 Jul 2026 11:11:19 GMT", + "openai-processing-ms": "327", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -1212,7 +1212,7 @@ "x-ratelimit-remaining-tokens": "179992734", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "bc2a090f-5ed2-4514-9bee-89b1a76aa13a" + "x-request-id": "req_6384e299db814812a86667cade595a0e" }, "status": 200, "statusText": "OK" @@ -1220,9 +1220,9 @@ }, { "callIndex": 3, - "id": "70155a3202ad7b6a", + "id": "ced7f223095288e7", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-09T11:20:20.187Z", + "recordedAt": "2026-07-10T11:11:21.239Z", "request": { "body": { "kind": "json", @@ -1243,11 +1243,11 @@ "role": "user" }, { - "id": "fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324", + "id": "fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7", "type": "item_reference" }, { - "call_id": "call_UiWXOc9U8IcpmtTc8pwI68SC", + "call_id": "call_Pes0Oql8gdb9NEgsBRsmXe4y", "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "type": "function_call_output" } @@ -1602,27 +1602,27 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f3712c81a1a25e232396ef7f18\",\"object\":\"response\",\"created_at\":1783596019,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f3712c81a1a25e232396ef7f18\",\"object\":\"response\",\"created_at\":1783596019,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_yRc6f1bJPthZhaIEzCySgUi0\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"3yEZTEfKtX2uPH\",\"output_index\":0,\"sequence_number\":3}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"url\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"pViqlsbdLm9Va\",\"output_index\":0,\"sequence_number\":4}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"JoC4t2F0ZjA0j\",\"output_index\":0,\"sequence_number\":5}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"https\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"3SIyr1G934J\",\"output_index\":0,\"sequence_number\":6}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"://\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"fb8FNgLM9KRva\",\"output_index\":0,\"sequence_number\":7}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"eve\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"DsXhS39pakx6S\",\"output_index\":0,\"sequence_number\":8}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\".dev\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"TahVz5KKQdux\",\"output_index\":0,\"sequence_number\":9}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/docs\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"CbI5smSCsQC\",\"output_index\":0,\"sequence_number\":10}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/g\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"sHqF3zJ3ylLC6D\",\"output_index\":0,\"sequence_number\":11}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"uid\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"FrulpXK3ohZuf\",\"output_index\":0,\"sequence_number\":12}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"es\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"NyjUoWGOGsI9LO\",\"output_index\":0,\"sequence_number\":13}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/in\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"q1r4ncVF0n7XZ\",\"output_index\":0,\"sequence_number\":14}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"strument\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"u2TPRcWH\",\"output_index\":0,\"sequence_number\":15}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"ation\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"iBlTApKlyB4\",\"output_index\":0,\"sequence_number\":16}", - "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"obfuscation\":\"Db3HDqB94PQlS8\",\"output_index\":0,\"sequence_number\":17}", - "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"item_id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"output_index\":0,\"sequence_number\":18}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_yRc6f1bJPthZhaIEzCySgUi0\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":19}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f3712c81a1a25e232396ef7f18\",\"object\":\"response\",\"created_at\":1783596019,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596020,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_yRc6f1bJPthZhaIEzCySgUi0\",\"name\":\"read\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6724,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6751},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3585944819e88d43d5e000db9ad\",\"object\":\"response\",\"created_at\":1783681880,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3585944819e88d43d5e000db9ad\",\"object\":\"response\",\"created_at\":1783681880,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_NnIWdTvbNCgAVPAn6811c48j\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"l8JRw7rToT7b1b\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"url\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"EV42iDrszuCM7\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"vOtYAZ0CkMAfv\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"https\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"IVJ3Y0SfteV\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"://\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"wm0F583rYfFB4\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"eve\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"Zg2EWpl5uIklV\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\".dev\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"XBbaqzpQ9gDE\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/docs\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"PMEt221sFqN\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/g\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"ocMv2ePd2qhkpC\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"uid\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"t1Tl3BGLZdrau\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"es\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"Gu0M9RPND1VHTe\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/in\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"VtwfzziZFklf3\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"strument\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"2gemIWVc\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"ation\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"bLJ01B2Am8P\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"obfuscation\":\"Cw9a9fXo45y2bB\",\"output_index\":0,\"sequence_number\":17}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_NnIWdTvbNCgAVPAn6811c48j\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":19}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3585944819e88d43d5e000db9ad\",\"object\":\"response\",\"created_at\":1783681880,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681881,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_NnIWdTvbNCgAVPAn6811c48j\",\"name\":\"read\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6724,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6751},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" ], "kind": "sse" }, @@ -1630,11 +1630,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a186f050ba454baf-VIE", + "cf-ray": "a18f208799825b18-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Thu, 09 Jul 2026 11:20:19 GMT", - "openai-processing-ms": "288", + "date": "Fri, 10 Jul 2026 11:11:20 GMT", + "openai-processing-ms": "298", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -1647,7 +1647,7 @@ "x-ratelimit-remaining-tokens": "179992635", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "272b03f6-cb9b-492a-bf56-58cf5cf71805" + "x-request-id": "req_3a2a06aa1cac4018a8a80392af1c04fc" }, "status": 200, "statusText": "OK" @@ -1655,9 +1655,9 @@ }, { "callIndex": 4, - "id": "c8400420bcabd841", + "id": "004a21b65057e805", "matchKey": "POST api.openai.com/v1/responses", - "recordedAt": "2026-07-09T11:20:21.921Z", + "recordedAt": "2026-07-10T11:11:22.603Z", "request": { "body": { "kind": "json", @@ -1678,20 +1678,20 @@ "role": "user" }, { - "id": "fc_0b5ffbdb5c222510006a4f83f02a6c81a1ad6d7f2870404324", + "id": "fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7", "type": "item_reference" }, { - "call_id": "call_UiWXOc9U8IcpmtTc8pwI68SC", + "call_id": "call_Pes0Oql8gdb9NEgsBRsmXe4y", "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "type": "function_call_output" }, { - "id": "fc_0b5ffbdb5c222510006a4f83f3e51881a18ea983620f9f81bb", + "id": "fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e", "type": "item_reference" }, { - "call_id": "call_yRc6f1bJPthZhaIEzCySgUi0", + "call_id": "call_NnIWdTvbNCgAVPAn6811c48j", "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", "type": "function_call_output" } @@ -2046,76 +2046,75 @@ "response": { "body": { "chunks": [ - "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f480f081a1b815e0368227d112\",\"object\":\"response\",\"created_at\":1783596020,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", - "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f480f081a1b815e0368227d112\",\"object\":\"response\",\"created_at\":1783596020,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", - "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", - "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Final\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"r74GheqwqaP\",\"output_index\":0,\"sequence_number\":4}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" answer\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"4HQwigGY5\",\"output_index\":0,\"sequence_number\":5}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" from\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"mj6cFK9bXZu\",\"output_index\":0,\"sequence_number\":6}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"YU1kGsQAFxF\",\"output_index\":0,\"sequence_number\":7}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"VyD3bRElVYy5hVS\",\"output_index\":0,\"sequence_number\":8}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" researcher\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dcUNX\",\"output_index\":0,\"sequence_number\":9}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"y36dQXOAf\",\"output_index\":0,\"sequence_number\":10}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dUCDqWFMH9nNeWc\",\"output_index\":0,\"sequence_number\":11}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"DxefIcSVNlhd\",\"output_index\":0,\"sequence_number\":12}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":13}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"xaNEi3oa6tIZun\",\"output_index\":0,\"sequence_number\":14}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"jGhFfgVzuD\",\"output_index\":0,\"sequence_number\":15}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"rgZxUElHLdtw5\",\"output_index\":0,\"sequence_number\":16}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"ghBZBaur1QtO1\",\"output_index\":0,\"sequence_number\":17}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dnomYHrR0oSI\",\"output_index\":0,\"sequence_number\":18}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"GWlkQtkzPYZ\",\"output_index\":0,\"sequence_number\":19}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"XqLv5VOKBHp9K4\",\"output_index\":0,\"sequence_number\":20}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"7wNU5sLVHziGM\",\"output_index\":0,\"sequence_number\":21}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"uBTHWUqIs6NZim\",\"output_index\":0,\"sequence_number\":22}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"rvcbhN8JubRdA\",\"output_index\":0,\"sequence_number\":23}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"dYsAlUgC\",\"output_index\":0,\"sequence_number\":24}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"cuRVOAP833M\",\"output_index\":0,\"sequence_number\":25}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"NFaKOq55M3sSs9z\",\"output_index\":0,\"sequence_number\":26}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"0fXQ1w4Jx9E\",\"output_index\":0,\"sequence_number\":27}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" title\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"RKZJh0Hc2y\",\"output_index\":0,\"sequence_number\":28}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"DkNDSsDHW2QonsM\",\"output_index\":0,\"sequence_number\":29}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"626NVK4C0REn\",\"output_index\":0,\"sequence_number\":30}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":31}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"jA1iLcrVIooD4Ir\",\"output_index\":0,\"sequence_number\":32}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" URL\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"wBZsmreGiDCV\",\"output_index\":0,\"sequence_number\":33}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"4tH0wvYVeAIM7zl\",\"output_index\":0,\"sequence_number\":34}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"uCUHfUCXLt\",\"output_index\":0,\"sequence_number\":35}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"lExNLsbKFbDw9\",\"output_index\":0,\"sequence_number\":36}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"OYWkA1Xpgb0m6\",\"output_index\":0,\"sequence_number\":37}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"iEGW8PgHSX4R\",\"output_index\":0,\"sequence_number\":38}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"8zo3rbkqnqD\",\"output_index\":0,\"sequence_number\":39}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"FbX61rgYvphVjc\",\"output_index\":0,\"sequence_number\":40}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"Gw4Y5MATJAuPt\",\"output_index\":0,\"sequence_number\":41}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"BjuswyeZzr4omS\",\"output_index\":0,\"sequence_number\":42}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"F9WF9BYyBQVVo\",\"output_index\":0,\"sequence_number\":43}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"ev9OqvcC\",\"output_index\":0,\"sequence_number\":44}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"bkf7PcYT4jx\",\"output_index\":0,\"sequence_number\":45}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"8CJHmcMFXzwswXA\",\"output_index\":0,\"sequence_number\":46}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"pBdt8VPT83o\",\"output_index\":0,\"sequence_number\":47}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" excerpt\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"YxHtlyvo\",\"output_index\":0,\"sequence_number\":48}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"aN8K6HyUNhurZgM\",\"output_index\":0,\"sequence_number\":49}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"mdzLNK6ZsRkB\",\"output_index\":0,\"sequence_number\":50}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" hooks\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"3r7ALtBodX\",\"output_index\":0,\"sequence_number\":51}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" expose\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"yiJireJKY\",\"output_index\":0,\"sequence_number\":52}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" runtime\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"BBb6sLQr\",\"output_index\":0,\"sequence_number\":53}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" stream\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"qN8I4vzpX\",\"output_index\":0,\"sequence_number\":54}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" events\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"MwvgfXvBr\",\"output_index\":0,\"sequence_number\":55}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" that\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"821mE8CX7SP\",\"output_index\":0,\"sequence_number\":56}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Brain\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"XVi4y09KgI\",\"output_index\":0,\"sequence_number\":57}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"trust\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"66I0WlSs1g8\",\"output_index\":0,\"sequence_number\":58}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" maps\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"zEhhyqOT6ih\",\"output_index\":0,\"sequence_number\":59}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" into\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"gSoYjurIcPq\",\"output_index\":0,\"sequence_number\":60}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" a\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"9JRlfxk6q9riK3\",\"output_index\":0,\"sequence_number\":61}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" nested\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"YPUlhuEHa\",\"output_index\":0,\"sequence_number\":62}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" turn\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"LEUvgUYvwOj\",\"output_index\":0,\"sequence_number\":63}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" trace\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"c5x8wjmlF4\",\"output_index\":0,\"sequence_number\":64}", - "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"obfuscation\":\"mTycEutdDgPLFdP\",\"output_index\":0,\"sequence_number\":65}", - "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":66,\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}", - "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"},\"sequence_number\":67}", - "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":68}", - "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0b5ffbdb5c222510006a4f83f480f081a1b815e0368227d112\",\"object\":\"response\",\"created_at\":1783596020,\"status\":\"completed\",\"background\":false,\"completed_at\":1783596021,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_0b5ffbdb5c222510006a4f83f509d481a181e89980b7f49a02\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6806,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":66,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6872},\"user\":null,\"metadata\":{}},\"sequence_number\":69}" + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3599804819ebbee567503de7336\",\"object\":\"response\",\"created_at\":1783681881,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3599804819ebbee567503de7336\",\"object\":\"response\",\"created_at\":1783681881,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Final\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Famxr0xYrqk\",\"output_index\":0,\"sequence_number\":4}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" answer\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"5amkde4dm\",\"output_index\":0,\"sequence_number\":5}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" from\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"hFKcUtdghAc\",\"output_index\":0,\"sequence_number\":6}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"PPCvmwrgbf5\",\"output_index\":0,\"sequence_number\":7}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"AggTnuXaWNkeDvD\",\"output_index\":0,\"sequence_number\":8}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" researcher\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Pwu9u\",\"output_index\":0,\"sequence_number\":9}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"geNV9klaQ\",\"output_index\":0,\"sequence_number\":10}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"3bdXApBnpxAZB1\",\"output_index\":0,\"sequence_number\":11}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"hIqPlNnq9Bx9\",\"output_index\":0,\"sequence_number\":12}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":13}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"OHhiS76TjKzJBn\",\"output_index\":0,\"sequence_number\":14}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"j7jnDzyni2\",\"output_index\":0,\"sequence_number\":15}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"juUFqlKJ5PFie\",\"output_index\":0,\"sequence_number\":16}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"a1kqwCMFx7Rw1\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"YXzMp5lze1gY\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Q4LndUbjznx\",\"output_index\":0,\"sequence_number\":19}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"BKFsP4mVEnU8S7\",\"output_index\":0,\"sequence_number\":20}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"pvsxguI8mcHpy\",\"output_index\":0,\"sequence_number\":21}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"nrwIFO3pOAVLQR\",\"output_index\":0,\"sequence_number\":22}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"jDynI5IaVmPPH\",\"output_index\":0,\"sequence_number\":23}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Owm1xthN\",\"output_index\":0,\"sequence_number\":24}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"1VRcOHO2AKM\",\"output_index\":0,\"sequence_number\":25}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"R2FLmcOzcfwAirT\",\"output_index\":0,\"sequence_number\":26}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"jmCDvEuX6nU\",\"output_index\":0,\"sequence_number\":27}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" title\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"gLGVeFJUUy\",\"output_index\":0,\"sequence_number\":28}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"EEB03lMNTDXfud\",\"output_index\":0,\"sequence_number\":29}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"QJfyCygg6U09\",\"output_index\":0,\"sequence_number\":30}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":31}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"cW9jf1cYrurgPDe\",\"output_index\":0,\"sequence_number\":32}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" URL\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"waivffA5xT6x\",\"output_index\":0,\"sequence_number\":33}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"vJbQVvLUssPSR5\",\"output_index\":0,\"sequence_number\":34}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"jpdlxQupmZ\",\"output_index\":0,\"sequence_number\":35}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Xjxb4jMbJtjYU\",\"output_index\":0,\"sequence_number\":36}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"BpcoybHCmrBLo\",\"output_index\":0,\"sequence_number\":37}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"KAR6ZGNifYxj\",\"output_index\":0,\"sequence_number\":38}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"DTT0svMgGbG\",\"output_index\":0,\"sequence_number\":39}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"3HXCMPxCuYEVM0\",\"output_index\":0,\"sequence_number\":40}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"BQrrMEy7DU1uu\",\"output_index\":0,\"sequence_number\":41}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"eaPyWnDksv0R5M\",\"output_index\":0,\"sequence_number\":42}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"i53x1WMt5FVSB\",\"output_index\":0,\"sequence_number\":43}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Y0oCH6uq\",\"output_index\":0,\"sequence_number\":44}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"fgH2Wo4ulSz\",\"output_index\":0,\"sequence_number\":45}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"GZ48UTGBRRhTpX0\",\"output_index\":0,\"sequence_number\":46}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" excerpt\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"VkDcgoUZ\",\"output_index\":0,\"sequence_number\":47}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"t8hJOKtW4ikvjx\",\"output_index\":0,\"sequence_number\":48}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"runADXEbC9RU\",\"output_index\":0,\"sequence_number\":49}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" hooks\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"tpcER8H5er\",\"output_index\":0,\"sequence_number\":50}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" expose\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"jwSn7AqPH\",\"output_index\":0,\"sequence_number\":51}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" runtime\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"IoaXaqd1\",\"output_index\":0,\"sequence_number\":52}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" stream\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"y3Qt9rDHf\",\"output_index\":0,\"sequence_number\":53}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" events\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"rXgufMcDE\",\"output_index\":0,\"sequence_number\":54}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" that\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Ot0dqQlxu7w\",\"output_index\":0,\"sequence_number\":55}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Brain\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"CZisLBF6wB\",\"output_index\":0,\"sequence_number\":56}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"trust\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"JhTka8hsrgI\",\"output_index\":0,\"sequence_number\":57}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" maps\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"3CsEdWP86Aw\",\"output_index\":0,\"sequence_number\":58}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" into\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"0bu6OigZuk0\",\"output_index\":0,\"sequence_number\":59}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" a\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"x7dBIJVvHLmf61\",\"output_index\":0,\"sequence_number\":60}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" nested\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"Rl9Wb5xez\",\"output_index\":0,\"sequence_number\":61}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" turn\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"xVR14y5OCxL\",\"output_index\":0,\"sequence_number\":62}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" trace\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"fIOEXhFtlS\",\"output_index\":0,\"sequence_number\":63}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"obfuscation\":\"17KacrpW1qfBRcc\",\"output_index\":0,\"sequence_number\":64}", + "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":65,\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}", + "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"},\"sequence_number\":66}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":67}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3599804819ebbee567503de7336\",\"object\":\"response\",\"created_at\":1783681881,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681882,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6806,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":65,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6871},\"user\":null,\"metadata\":{}},\"sequence_number\":68}" ], "kind": "sse" }, @@ -2123,11 +2122,11 @@ "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", "alt-svc": "h3=\":443\"; ma=86400", "cf-cache-status": "DYNAMIC", - "cf-ray": "a186f0573f354baf-VIE", + "cf-ray": "a18f208f587b5b18-VIE", "connection": "keep-alive", "content-type": "text/event-stream; charset=utf-8", - "date": "Thu, 09 Jul 2026 11:20:20 GMT", - "openai-processing-ms": "412", + "date": "Fri, 10 Jul 2026 11:11:22 GMT", + "openai-processing-ms": "299", "openai-version": "2020-10-01", "server": "cloudflare", "set-cookie": "[REDACTED]", @@ -2137,10 +2136,2250 @@ "x-ratelimit-limit-requests": "30000", "x-ratelimit-limit-tokens": "180000000", "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "179992551", + "x-ratelimit-remaining-tokens": "179992554", "x-ratelimit-reset-requests": "2ms", "x-ratelimit-reset-tokens": "2ms", - "x-request-id": "e2972c44-de1c-4ae9-9511-52c74e478903" + "x-request-id": "req_77d37a0f0cc14a149709ac7db90cdd97" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 5, + "id": "9334a3ac526f3f90", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:24.645Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7", + "type": "item_reference" + }, + { + "call_id": "call_Pes0Oql8gdb9NEgsBRsmXe4y", + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "type": "function_call_output" + }, + { + "id": "fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e", + "type": "item_reference" + }, + { + "call_id": "call_NnIWdTvbNCgAVPAn6811c48j", + "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + }, + { + "id": "msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7", + "type": "item_reference" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario again", + "type": "input_text" + } + ], + "role": "user" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Reads a deterministic Eve documentation page fixture.", + "name": "read", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Research the Eve instrumentation documentation before the parent reads it.", + "name": "researcher", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d35bbd68819ebb19f207e83f8ffc\",\"object\":\"response\",\"created_at\":1783681883,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d35bbd68819ebb19f207e83f8ffc\",\"object\":\"response\",\"created_at\":1783681883,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_Myi0H7WWD8oij1u4xs7pRVit\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"vXOBTZ5js6VCdp\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"message\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"1upALsO8Q\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"PGVeNXaUvxEV1\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"LWOnLCj7M2Bay\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"HElkzBDlJLAZ\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"LrdyFcGf87\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"XcKth9j2qQz\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"6ErSpy2gR4DV\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"FAk2PIVzMsGekY\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"i9uCIuyhJUI7NLI\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"ryR6J739BVutBhv\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"xSQcxp0\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" again\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"XtWj68YGgf\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"obfuscation\":\"x9yCWH9thv8LUS\",\"output_index\":0,\"sequence_number\":17}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario again\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario again\\\"}\",\"call_id\":\"call_Myi0H7WWD8oij1u4xs7pRVit\",\"name\":\"researcher\"},\"output_index\":0,\"sequence_number\":19}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d35bbd68819ebb19f207e83f8ffc\",\"object\":\"response\",\"created_at\":1783681883,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681884,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"message\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario again\\\"}\",\"call_id\":\"call_Myi0H7WWD8oij1u4xs7pRVit\",\"name\":\"researcher\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6888,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":29,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6917},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f209cccc95b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:24 GMT", + "openai-processing-ms": "270", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992470", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "req_32231e6196bf494abd354f874b1216a0" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 6, + "id": "0d098cd22c9509ad", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:25.919Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + "type": "input_text" + } + ], + "role": "user" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0a2d7ca9c1a5ebe3006a50d35d154c819ea363b35c0007d703\",\"object\":\"response\",\"created_at\":1783681885,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0a2d7ca9c1a5ebe3006a50d35d154c819ea363b35c0007d703\",\"object\":\"response\",\"created_at\":1783681885,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_6Y54lfJKPFWU2pIYcuzVCBcg\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"rfKieIO06bZTfd\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"query\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"kKEobCVbjQz\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"UpMEttz3xXYvy\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Run\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"wzWVBCJMbV0o9\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" the\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"0ldNocNjwCI6\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Brain\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"U9LT3i9GMy\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"trust\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"Bg4sWR3kJCV\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" Eve\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"B8P9H1gEjFco\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" instrumentation\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" e\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"6Ar4mHlNS2H07y\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"2\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"wK31NFgSfiwNNhm\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"e\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"LH8pxEeHUU3jlc2\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" scenario\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"9nt0vCK\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\" again\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"QFLPwE78fO\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"obfuscation\":\"I2ffZPhQ671D3p\",\"output_index\":0,\"sequence_number\":17}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario again\\\"}\",\"item_id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario again\\\"}\",\"call_id\":\"call_6Y54lfJKPFWU2pIYcuzVCBcg\",\"name\":\"search\"},\"output_index\":0,\"sequence_number\":19}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0a2d7ca9c1a5ebe3006a50d35d154c819ea363b35c0007d703\",\"object\":\"response\",\"created_at\":1783681885,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681885,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"query\\\":\\\"Run the Braintrust Eve instrumentation e2e scenario again\\\"}\",\"call_id\":\"call_6Y54lfJKPFWU2pIYcuzVCBcg\",\"name\":\"search\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6555,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6582},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f20a50c1a5b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:25 GMT", + "openai-processing-ms": "277", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992803", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "req_387c038fdff74a59a4bd7655ec1d2e7e" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 7, + "id": "979295fca5c33506", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:27.047Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_0a2d7ca9c1a5ebe3006a50d35da938819e8120ef9aba2730f1", + "type": "item_reference" + }, + { + "call_id": "call_6Y54lfJKPFWU2pIYcuzVCBcg", + "output": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario again\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_0a2d7ca9c1a5ebe3006a50d35e3f54819ea9ae91ac085a8c10\",\"object\":\"response\",\"created_at\":1783681886,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0a2d7ca9c1a5ebe3006a50d35e3f54819ea9ae91ac085a8c10\",\"object\":\"response\",\"created_at\":1783681886,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Research\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"tD7hOWGB\",\"output_index\":0,\"sequence_number\":4}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"er\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"jWZSdmC69PSK66\",\"output_index\":0,\"sequence_number\":5}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"n1q0l93Oi\",\"output_index\":0,\"sequence_number\":6}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"ws99q4FbncAFLi1\",\"output_index\":0,\"sequence_number\":7}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"0Kn2oNzAGoVb\",\"output_index\":0,\"sequence_number\":8}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":9}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"UUmuFwTvGPD5lI\",\"output_index\":0,\"sequence_number\":10}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"TRzeJvofIJ\",\"output_index\":0,\"sequence_number\":11}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"lVhs2zPNrjvDR\",\"output_index\":0,\"sequence_number\":12}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"QWO33jIV0QXzW\",\"output_index\":0,\"sequence_number\":13}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"fpvCX3Y6G1Ld\",\"output_index\":0,\"sequence_number\":14}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"5GreMWAn6v4\",\"output_index\":0,\"sequence_number\":15}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"52YbpKHi8xGBiN\",\"output_index\":0,\"sequence_number\":16}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"tIQx61wJEV61b\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"WqQY2pIn675vYT\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"1UY3ASabXl1B3\",\"output_index\":0,\"sequence_number\":19}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"nmIPRL5g\",\"output_index\":0,\"sequence_number\":20}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"obfuscation\":\"Pfz4EZ8hTwT\",\"output_index\":0,\"sequence_number\":21}", + "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":22,\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}", + "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"},\"sequence_number\":23}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":24}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0a2d7ca9c1a5ebe3006a50d35e3f54819ea9ae91ac085a8c10\",\"object\":\"response\",\"created_at\":1783681886,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681886,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_0a2d7ca9c1a5ebe3006a50d35ecda4819eb2bba4193947e573\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6627,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":22,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6649},\"user\":null,\"metadata\":{}},\"sequence_number\":25}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f20ac6b425b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:26 GMT", + "openai-processing-ms": "282", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992731", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "req_d1b70eb8e9a844b2a510dc3577fbd444" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 8, + "id": "be179c6a7eefc443", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:28.971Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7", + "type": "item_reference" + }, + { + "call_id": "call_Pes0Oql8gdb9NEgsBRsmXe4y", + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "type": "function_call_output" + }, + { + "id": "fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e", + "type": "item_reference" + }, + { + "call_id": "call_NnIWdTvbNCgAVPAn6811c48j", + "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + }, + { + "id": "msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7", + "type": "item_reference" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario again", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1", + "type": "item_reference" + }, + { + "call_id": "call_Myi0H7WWD8oij1u4xs7pRVit", + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "type": "function_call_output" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Reads a deterministic Eve documentation page fixture.", + "name": "read", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Research the Eve instrumentation documentation before the parent reads it.", + "name": "researcher", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d35fd484819ea15717ecbdd6de82\",\"object\":\"response\",\"created_at\":1783681887,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d35fd484819ea15717ecbdd6de82\",\"object\":\"response\",\"created_at\":1783681887,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_F192KGfXDgizfuwhj3Dntr1Y\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"THc2zjrtgyTa9B\",\"output_index\":0,\"sequence_number\":3}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"url\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"YFshAg5TLCOHK\",\"output_index\":0,\"sequence_number\":4}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"Hco2Ua7KfWbZk\",\"output_index\":0,\"sequence_number\":5}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"https\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"mPnRaBFLzrG\",\"output_index\":0,\"sequence_number\":6}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"://\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"FUk7nTpHwioQ3\",\"output_index\":0,\"sequence_number\":7}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"eve\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"z53c2tipJxB5f\",\"output_index\":0,\"sequence_number\":8}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\".dev\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"xb15azhZ9I4k\",\"output_index\":0,\"sequence_number\":9}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/docs\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"MjB2fjCcLHV\",\"output_index\":0,\"sequence_number\":10}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/g\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"qrrmn26lyPbAvF\",\"output_index\":0,\"sequence_number\":11}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"uid\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"CU7bl0ghuX3ry\",\"output_index\":0,\"sequence_number\":12}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"es\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"QClXgEZuatAeBj\",\"output_index\":0,\"sequence_number\":13}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"/in\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"fY774VxD7lMT4\",\"output_index\":0,\"sequence_number\":14}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"strument\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"LzMqCwGr\",\"output_index\":0,\"sequence_number\":15}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"ation\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"hxapBJrLnKw\",\"output_index\":0,\"sequence_number\":16}", + "event: response.function_call_arguments.delta\ndata: {\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"obfuscation\":\"xgcy6NSVMT8g9J\",\"output_index\":0,\"sequence_number\":17}", + "event: response.function_call_arguments.done\ndata: {\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"item_id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_F192KGfXDgizfuwhj3Dntr1Y\",\"name\":\"read\"},\"output_index\":0,\"sequence_number\":19}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d35fd484819ea15717ecbdd6de82\",\"object\":\"response\",\"created_at\":1783681887,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681888,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"url\\\":\\\"https://eve.dev/docs/guides/instrumentation\\\"}\",\"call_id\":\"call_F192KGfXDgizfuwhj3Dntr1Y\",\"name\":\"read\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":6947,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":27,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":6974},\"user\":null,\"metadata\":{}},\"sequence_number\":20}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f20b55b155b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:28 GMT", + "openai-processing-ms": "599", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992413", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "req_141f39cd2c2440d3a72a27da47a36373" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 9, + "id": "324a353b19537691", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:30.385Z", + "request": { + "body": { + "kind": "json", + "value": { + "include": ["web_search_call.action.sources"], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "developer" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_031309eb72c0eb29006a50d353c9d4819e8d19a170a5cfd2d7", + "type": "item_reference" + }, + { + "call_id": "call_Pes0Oql8gdb9NEgsBRsmXe4y", + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "type": "function_call_output" + }, + { + "id": "fc_031309eb72c0eb29006a50d358f640819eacf02e300c0dfe9e", + "type": "item_reference" + }, + { + "call_id": "call_NnIWdTvbNCgAVPAn6811c48j", + "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + }, + { + "id": "msg_031309eb72c0eb29006a50d35a26a4819eb4ab37a90ac34ff7", + "type": "item_reference" + }, + { + "content": [ + { + "text": "Run the Braintrust Eve instrumentation e2e scenario again", + "type": "input_text" + } + ], + "role": "user" + }, + { + "id": "fc_031309eb72c0eb29006a50d35c5310819e81e311d4c7f9f5f1", + "type": "item_reference" + }, + { + "call_id": "call_Myi0H7WWD8oij1u4xs7pRVit", + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "type": "function_call_output" + }, + { + "id": "fc_031309eb72c0eb29006a50d3609af4819ea329f7f64a8b39f7", + "type": "item_reference" + }, + { + "call_id": "call_F192KGfXDgizfuwhj3Dntr1Y", + "output": "{\"excerpt\":\"Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\",\"section\":\"Runtime context\",\"title\":\"Eve instrumentation\",\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "type": "function_call_output" + } + ], + "model": "gpt-5.4-mini", + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.", + "name": "ask_question", + "parameters": { + "additionalProperties": false, + "properties": { + "allowFreeform": { + "description": "Whether the user may answer with freeform text instead of selecting one of the provided options.", + "type": "boolean" + }, + "options": { + "description": "Selectable answer options to present to the user.", + "items": { + "additionalProperties": false, + "properties": { + "description": { + "description": "Optional additional context for this option.", + "type": "string" + }, + "id": { + "description": "Stable identifier for the option.", + "type": "string" + }, + "label": { + "description": "User-facing label for the option.", + "type": "string" + }, + "style": { + "description": "Visual treatment hint for the option.", + "enum": ["primary", "danger", "default"], + "type": "string" + } + }, + "required": ["id", "label"], + "type": "object" + }, + "type": "array" + }, + "prompt": { + "description": "The prompt to present to the user.", + "type": "string" + } + }, + "required": ["prompt"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Execute a shell command in the shared workspace environment.", + "name": "bash", + "parameters": { + "additionalProperties": false, + "properties": { + "command": { + "description": "The shell command to execute.", + "type": "string" + } + }, + "required": ["command"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast file pattern matching tool that works with any codebase size.\n\nUsage:\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\".\n- Returns matching file paths.\n- Use this tool when you need to find files by name patterns.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Use the grep tool instead if you need to search file contents.\n- Call this tool in parallel when you know there are multiple patterns to search for.", + "name": "glob", + "parameters": { + "additionalProperties": false, + "properties": { + "limit": { + "description": "Maximum number of results to return. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "path": { + "description": "The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The glob pattern to match files against (e.g. \"**/*.ts\", \"src/**/*.js\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Fast content search tool that works with any codebase size.\n\nUsage:\n- Searches file contents using regular expressions.\n- Supports full regex syntax (e.g. \"log.*Error\", \"function\\s+\\w+\").\n- Filter files by pattern with the glob parameter (e.g. \"*.js\", \"*.{ts,tsx}\").\n- Returns matching lines with file paths and line numbers.\n- Use this tool when you need to find files containing specific patterns.\n- Use the glob tool instead if you only need to find files by name.\n- Call this tool in parallel when you have multiple independent searches.\n- Any line longer than 2000 characters is truncated.", + "name": "grep", + "parameters": { + "additionalProperties": false, + "properties": { + "context": { + "description": "Number of surrounding context lines to include before and after each match. Defaults to 0.", + "minimum": 0, + "type": "integer" + }, + "glob": { + "description": "Filter files by glob pattern (e.g. \"*.ts\", \"*.{ts,tsx}\").", + "type": "string" + }, + "ignoreCase": { + "description": "Perform case-insensitive search. Defaults to false.", + "type": "boolean" + }, + "limit": { + "description": "Maximum number of matches to return per file. Defaults to 100.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "literal": { + "description": "Treat the pattern as a literal string instead of a regular expression. Defaults to false.", + "type": "boolean" + }, + "path": { + "description": "The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.", + "type": "string" + }, + "pattern": { + "description": "The regex pattern to search for in file contents (e.g. \"log.*Error\", \"function\\s+\\w+\").", + "type": "string" + } + }, + "required": ["pattern"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Read a file from the local filesystem. If the path does not exist, an error is returned.\n\nUsage:\n- The filePath parameter should be an absolute path.\n- By default, this tool returns up to 2000 lines from the start of the file.\n- The offset parameter is the line number to start from (1-indexed).\n- To read later sections, call this tool again with a larger offset.\n- Use the grep tool to find specific content in large files or files with long lines.\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \"foo\\n\", you will receive \"1: foo\\n\".\n- Any line longer than 2000 characters is truncated.\n- Call this tool in parallel when you know there are multiple files you want to read.\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.", + "name": "read_file", + "parameters": { + "additionalProperties": false, + "properties": { + "filePath": { + "description": "The absolute path to the file to read.", + "type": "string" + }, + "limit": { + "description": "Maximum number of lines to return. Defaults to 2000.", + "minimum": 1, + "type": "integer" + }, + "offset": { + "description": "1-based line number to start from. Defaults to 1.", + "minimum": 1, + "type": "integer" + } + }, + "required": ["filePath"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.", + "name": "write_file", + "parameters": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Complete replacement file contents.", + "type": "string" + }, + "filePath": { + "description": "The absolute path to the file to write (must be absolute, not relative).", + "type": "string" + } + }, + "required": ["filePath", "content"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Use this tool to create and manage a structured task list for the current session.\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\n\nWhen to use:\n- Complex multistep tasks requiring 3 or more distinct steps\n- When the user provides multiple tasks or a numbered list\n- After receiving new instructions, to capture requirements\n- After completing a task, to mark it complete and add follow-ups\n\nWhen NOT to use:\n- Single, straightforward tasks that need no tracking\n- Purely conversational or informational requests\n\nUsage:\n- Call with `todos` to replace the entire list (full replacement write)\n- Call without `todos` to read the current list\n- Both return the full current list with status counts\n- Mark tasks in_progress when you start, completed when done\n- Only have ONE task in_progress at a time", + "name": "todo", + "parameters": { + "additionalProperties": false, + "properties": { + "todos": { + "description": "The updated todo list. Omit to read the current list without modifying it.", + "items": { + "additionalProperties": false, + "properties": { + "content": { + "description": "Brief description of the task.", + "type": "string" + }, + "priority": { + "description": "Priority level of the task.", + "enum": ["high", "medium", "low"], + "type": "string" + }, + "status": { + "description": "Current status of the task.", + "enum": [ + "pending", + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + } + }, + "required": ["content", "status", "priority"], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + }, + { + "description": "Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\n\nUsage notes:\n- The URL must be a fully-formed valid URL starting with http:// or https://\n- HTML responses are automatically converted to markdown or plain text based on the requested format\n- Format options: \"markdown\" (default), \"text\", or \"html\"\n- Default timeout is 30 seconds (max 120 seconds)\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\n- This tool is read-only and does not modify any files", + "name": "web_fetch", + "parameters": { + "additionalProperties": false, + "properties": { + "format": { + "description": "The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \"markdown\".", + "enum": ["markdown", "text", "html"], + "type": "string" + }, + "timeout": { + "description": "Optional timeout in seconds. Defaults to 30, max 120.", + "type": "number" + }, + "url": { + "description": "The fully-formed URL to fetch content from. Must start with http:// or https://.", + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "type": "web_search" + }, + { + "description": "Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \"skill\" value from the Available skills block.", + "name": "load_skill", + "parameters": { + "additionalProperties": false, + "properties": { + "skill": { + "description": "Available skill name or id.", + "type": "string" + } + }, + "required": ["skill"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Reads a deterministic Eve documentation page fixture.", + "name": "read", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Searches the Eve instrumentation documentation fixture.", + "name": "search", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Research the Eve instrumentation documentation before the parent reads it.", + "name": "researcher", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + }, + { + "description": "Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.", + "name": "agent", + "parameters": { + "additionalProperties": false, + "properties": { + "message": { + "description": "The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.", + "type": "string" + }, + "outputSchema": { + "description": "When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.", + "type": "object" + } + }, + "required": ["message"], + "type": "object" + }, + "type": "function" + } + ] + } + }, + "headers": {}, + "method": "POST", + "url": "https://api.openai.com/v1/responses" + }, + "response": { + "body": { + "chunks": [ + "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3615ddc819eb016cfa1f51a50c9\",\"object\":\"response\",\"created_at\":1783681889,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}", + "event: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3615ddc819eb016cfa1f51a50c9\",\"object\":\"response\",\"created_at\":1783681889,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}", + "event: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}", + "event: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Final\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"2ocw2ZjNY80\",\"output_index\":0,\"sequence_number\":4}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" answer\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"ErRkGiNNP\",\"output_index\":0,\"sequence_number\":5}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" from\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"uXhyWdgc88n\",\"output_index\":0,\"sequence_number\":6}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"Jio35o2aP59\",\"output_index\":0,\"sequence_number\":7}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\":\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"kiP73JhJsoCPAQK\",\"output_index\":0,\"sequence_number\":8}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" researcher\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"TM2LF\",\"output_index\":0,\"sequence_number\":9}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" result\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"AySgGiwPY\",\"output_index\":0,\"sequence_number\":10}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"dGkidY13IWSjCe\",\"output_index\":0,\"sequence_number\":11}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"FYpJHZ8OVid8\",\"output_index\":0,\"sequence_number\":12}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":13}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" —\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"I7gjpsGeJGye9i\",\"output_index\":0,\"sequence_number\":14}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"bqoKuknnKq\",\"output_index\":0,\"sequence_number\":15}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"klVCvzB5o9vxb\",\"output_index\":0,\"sequence_number\":16}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"z1y3bx7Vqu6ZS\",\"output_index\":0,\"sequence_number\":17}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"YEv5l3Zl1eA4\",\"output_index\":0,\"sequence_number\":18}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"IPD9YmzuSaa\",\"output_index\":0,\"sequence_number\":19}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"6TZQnGy1jgLf6L\",\"output_index\":0,\"sequence_number\":20}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"UrA8QqOx6NQZB\",\"output_index\":0,\"sequence_number\":21}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"gPZ1zPff4d4F8j\",\"output_index\":0,\"sequence_number\":22}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"QdFioGIra9qbR\",\"output_index\":0,\"sequence_number\":23}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"20o8hFw9\",\"output_index\":0,\"sequence_number\":24}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"ECW0mNWQUHz\",\"output_index\":0,\"sequence_number\":25}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"cS7qQwzbCwhNKEl\",\"output_index\":0,\"sequence_number\":26}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" read\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"QG39LKWsMy0\",\"output_index\":0,\"sequence_number\":27}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" title\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"VSoNQUj8fs\",\"output_index\":0,\"sequence_number\":28}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"yDyovns79Hd21v\",\"output_index\":0,\"sequence_number\":29}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"h39swag6XJbZ\",\"output_index\":0,\"sequence_number\":30}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" instrumentation\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":31}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"XM0LEB2yxTaNDPN\",\"output_index\":0,\"sequence_number\":32}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" URL\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"vBpYSlNqVScB\",\"output_index\":0,\"sequence_number\":33}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"Y2j8iCKV6ncGUG\",\"output_index\":0,\"sequence_number\":34}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" https\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"KN5Xc5COFy\",\"output_index\":0,\"sequence_number\":35}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"://\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"K5wzcyHgTI0W3\",\"output_index\":0,\"sequence_number\":36}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"YL8QtW3FlcCES\",\"output_index\":0,\"sequence_number\":37}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".dev\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"jAxV7P5Gi2mG\",\"output_index\":0,\"sequence_number\":38}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/docs\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"3oLLgNlVuuO\",\"output_index\":0,\"sequence_number\":39}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/g\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"LOGbkDXPTgUmUD\",\"output_index\":0,\"sequence_number\":40}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"uid\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"6TlyeOhIhL0uC\",\"output_index\":0,\"sequence_number\":41}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"es\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"BVKZf38rhBIoZv\",\"output_index\":0,\"sequence_number\":42}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"/in\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"T76YAv6h61CZn\",\"output_index\":0,\"sequence_number\":43}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"strument\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"lTSSN8d9\",\"output_index\":0,\"sequence_number\":44}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"ation\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"pKIwxfcsbQZ\",\"output_index\":0,\"sequence_number\":45}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\";\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"TSGIurekWToOKQE\",\"output_index\":0,\"sequence_number\":46}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" excerpt\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"XCtBGXFF\",\"output_index\":0,\"sequence_number\":47}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" =\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"gFIxMHOlZcaUj9\",\"output_index\":0,\"sequence_number\":48}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Eve\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"YPHJLD4om9Sc\",\"output_index\":0,\"sequence_number\":49}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" hooks\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"PEEDOIRccB\",\"output_index\":0,\"sequence_number\":50}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" expose\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"jXgGuhhLy\",\"output_index\":0,\"sequence_number\":51}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" runtime\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"6LYitIU1\",\"output_index\":0,\"sequence_number\":52}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" stream\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"YYZ2sZFmw\",\"output_index\":0,\"sequence_number\":53}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" events\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"OebNH61W9\",\"output_index\":0,\"sequence_number\":54}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" that\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"oSZMQybBt95\",\"output_index\":0,\"sequence_number\":55}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" Brain\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"9t5vEhllxM\",\"output_index\":0,\"sequence_number\":56}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"trust\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"7Xx84Uq0EeF\",\"output_index\":0,\"sequence_number\":57}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" maps\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"sztrJp6h3dR\",\"output_index\":0,\"sequence_number\":58}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" into\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"UhTT9al28dO\",\"output_index\":0,\"sequence_number\":59}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" a\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"npgLTGOXbngKtV\",\"output_index\":0,\"sequence_number\":60}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" nested\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"BDH2I1h68\",\"output_index\":0,\"sequence_number\":61}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" turn\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"zbsn2alEsyh\",\"output_index\":0,\"sequence_number\":62}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" trace\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"W3fU2YIR9L\",\"output_index\":0,\"sequence_number\":63}", + "event: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"obfuscation\":\"vBWnLNDEBwsQPyg\",\"output_index\":0,\"sequence_number\":64}", + "event: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":65,\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}", + "event: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"},\"sequence_number\":66}", + "event: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":67}", + "event: response.completed\ndata: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_031309eb72c0eb29006a50d3615ddc819eb016cfa1f51a50c9\",\"object\":\"response\",\"created_at\":1783681889,\"status\":\"completed\",\"background\":false,\"completed_at\":1783681890,\"error\":null,\"frequency_penalty\":0,\"incomplete_details\":null,\"instructions\":null,\"max_output_tokens\":null,\"max_tool_calls\":null,\"model\":\"gpt-5.4-mini-2026-03-17\",\"moderation\":null,\"output\":[{\"id\":\"msg_031309eb72c0eb29006a50d361f208819ebd797c08e54cf30c\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"none\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":true,\"temperature\":1,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Ask the user a question and wait for their response before continuing. Use this when you need clarification or a choice from the user.\",\"name\":\"ask_question\",\"parameters\":{\"type\":\"object\",\"properties\":{\"allowFreeform\":{\"type\":\"boolean\",\"description\":\"Whether the user may answer with freeform text instead of selecting one of the provided options.\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\",\"description\":\"Optional additional context for this option.\"},\"id\":{\"type\":\"string\",\"description\":\"Stable identifier for the option.\"},\"label\":{\"type\":\"string\",\"description\":\"User-facing label for the option.\"},\"style\":{\"type\":\"string\",\"enum\":[\"primary\",\"danger\",\"default\"],\"description\":\"Visual treatment hint for the option.\"}},\"required\":[\"description\",\"id\",\"label\",\"style\"],\"additionalProperties\":false},\"description\":\"Selectable answer options to present to the user.\"},\"prompt\":{\"type\":\"string\",\"description\":\"The prompt to present to the user.\"}},\"required\":[\"allowFreeform\",\"options\",\"prompt\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Execute a shell command in the shared workspace environment.\",\"name\":\"bash\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"command\":{\"description\":\"The shell command to execute.\",\"type\":\"string\"}},\"required\":[\"command\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast file pattern matching tool that works with any codebase size.\\n\\nUsage:\\n- Supports glob patterns like \\\"**/*.js\\\" or \\\"src/**/*.ts\\\".\\n- Returns matching file paths.\\n- Use this tool when you need to find files by name patterns.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Use the grep tool instead if you need to search file contents.\\n- Call this tool in parallel when you know there are multiple patterns to search for.\",\"name\":\"glob\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"limit\":{\"description\":\"Maximum number of results to return. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"path\":{\"description\":\"The directory to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against (e.g. \\\"**/*.ts\\\", \\\"src/**/*.js\\\").\",\"type\":\"string\"}},\"required\":[\"limit\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Fast content search tool that works with any codebase size.\\n\\nUsage:\\n- Searches file contents using regular expressions.\\n- Supports full regex syntax (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\\n- Filter files by pattern with the glob parameter (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\").\\n- Returns matching lines with file paths and line numbers.\\n- Use this tool when you need to find files containing specific patterns.\\n- Use the glob tool instead if you only need to find files by name.\\n- Call this tool in parallel when you have multiple independent searches.\\n- Any line longer than 2000 characters is truncated.\",\"name\":\"grep\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"context\":{\"description\":\"Number of surrounding context lines to include before and after each match. Defaults to 0.\",\"minimum\":0,\"type\":\"integer\"},\"glob\":{\"description\":\"Filter files by glob pattern (e.g. \\\"*.ts\\\", \\\"*.{ts,tsx}\\\").\",\"type\":\"string\"},\"ignoreCase\":{\"description\":\"Perform case-insensitive search. Defaults to false.\",\"type\":\"boolean\"},\"limit\":{\"description\":\"Maximum number of matches to return per file. Defaults to 100.\",\"maximum\":1000,\"minimum\":1,\"type\":\"integer\"},\"literal\":{\"description\":\"Treat the pattern as a literal string instead of a regular expression. Defaults to false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory or file to search in. Defaults to /workspace. Must be an absolute path. Omit to use the default.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents (e.g. \\\"log.*Error\\\", \\\"function\\\\s+\\\\w+\\\").\",\"type\":\"string\"}},\"required\":[\"context\",\"glob\",\"ignoreCase\",\"limit\",\"literal\",\"path\",\"pattern\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Read a file from the local filesystem. If the path does not exist, an error is returned.\\n\\nUsage:\\n- The filePath parameter should be an absolute path.\\n- By default, this tool returns up to 2000 lines from the start of the file.\\n- The offset parameter is the line number to start from (1-indexed).\\n- To read later sections, call this tool again with a larger offset.\\n- Use the grep tool to find specific content in large files or files with long lines.\\n- If you are unsure of the correct file path, use the glob tool to look up filenames by glob pattern.\\n- Contents are returned with each line prefixed by its line number as `: `. For example, if a file has contents \\\"foo\\\\n\\\", you will receive \\\"1: foo\\\\n\\\".\\n- Any line longer than 2000 characters is truncated.\\n- Call this tool in parallel when you know there are multiple files you want to read.\\n- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\",\"name\":\"read_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"filePath\":{\"description\":\"The absolute path to the file to read.\",\"type\":\"string\"},\"limit\":{\"description\":\"Maximum number of lines to return. Defaults to 2000.\",\"minimum\":1,\"type\":\"integer\"},\"offset\":{\"description\":\"1-based line number to start from. Defaults to 1.\",\"minimum\":1,\"type\":\"integer\"}},\"required\":[\"filePath\",\"limit\",\"offset\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Writes a file to the local filesystem.\\n\\nUsage:\\n- This tool will overwrite the existing file if there is one at the provided path.\\n- If this is an existing file, you MUST use the read_file tool first to read the file's contents. This tool will fail if you did not read the file first.\\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\",\"name\":\"write_file\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Complete replacement file contents.\",\"type\":\"string\"},\"filePath\":{\"description\":\"The absolute path to the file to write (must be absolute, not relative).\",\"type\":\"string\"}},\"required\":[\"content\",\"filePath\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Use this tool to create and manage a structured task list for the current session.\\nThis helps you track progress, organize complex tasks, and demonstrate thoroughness.\\n\\nWhen to use:\\n- Complex multistep tasks requiring 3 or more distinct steps\\n- When the user provides multiple tasks or a numbered list\\n- After receiving new instructions, to capture requirements\\n- After completing a task, to mark it complete and add follow-ups\\n\\nWhen NOT to use:\\n- Single, straightforward tasks that need no tracking\\n- Purely conversational or informational requests\\n\\nUsage:\\n- Call with `todos` to replace the entire list (full replacement write)\\n- Call without `todos` to read the current list\\n- Both return the full current list with status counts\\n- Mark tasks in_progress when you start, completed when done\\n- Only have ONE task in_progress at a time\",\"name\":\"todo\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"todos\":{\"description\":\"The updated todo list. Omit to read the current list without modifying it.\",\"items\":{\"additionalProperties\":false,\"properties\":{\"content\":{\"description\":\"Brief description of the task.\",\"type\":\"string\"},\"priority\":{\"description\":\"Priority level of the task.\",\"enum\":[\"high\",\"medium\",\"low\"],\"type\":\"string\"},\"status\":{\"description\":\"Current status of the task.\",\"enum\":[\"pending\",\"in_progress\",\"completed\",\"cancelled\"],\"type\":\"string\"}},\"required\":[\"content\",\"priority\",\"status\"],\"type\":\"object\"},\"type\":\"array\"}},\"type\":\"object\",\"required\":[\"todos\"]},\"strict\":true},{\"type\":\"function\",\"description\":\"Fetch a webpage and return its content in the requested format. Use this to retrieve and analyze content from URLs.\\n\\nUsage notes:\\n- The URL must be a fully-formed valid URL starting with http:// or https://\\n- HTML responses are automatically converted to markdown or plain text based on the requested format\\n- Format options: \\\"markdown\\\" (default), \\\"text\\\", or \\\"html\\\"\\n- Default timeout is 30 seconds (max 120 seconds)\\n- Maximum response size is 5 MB; content is further capped at the shared tool-output budget (50 KB / 2000 lines)\\n- This tool is read-only and does not modify any files\",\"name\":\"web_fetch\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html). HTML responses are automatically converted to the requested format. Defaults to \\\"markdown\\\".\",\"enum\":[\"markdown\",\"text\",\"html\"],\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds. Defaults to 30, max 120.\",\"type\":\"number\"},\"url\":{\"description\":\"The fully-formed URL to fetch content from. Must start with http:// or https://.\",\"type\":\"string\"}},\"required\":[\"format\",\"timeout\",\"url\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Load the full instructions for one available skill by name or id. Use this tool when the request clearly matches a listed skill description or when the user explicitly asks for that skill. This is not for MCP connections; use connection_search to access an installed connection. Loading adds the skill instructions to the current turn. Choose the \\\"skill\\\" value from the Available skills block.\",\"name\":\"load_skill\",\"parameters\":{\"additionalProperties\":false,\"properties\":{\"skill\":{\"description\":\"Available skill name or id.\",\"type\":\"string\"}},\"required\":[\"skill\"],\"type\":\"object\"},\"strict\":true},{\"type\":\"function\",\"description\":\"Reads a deterministic Eve documentation page fixture.\",\"name\":\"read\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"url\":{\"type\":\"string\"}},\"required\":[\"url\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Searches the Eve instrumentation documentation fixture.\",\"name\":\"search\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"}},\"required\":[\"query\"],\"additionalProperties\":false},\"strict\":true},{\"type\":\"function\",\"description\":\"Research the Eve instrumentation documentation before the parent reads it.\",\"name\":\"researcher\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"function\",\"description\":\"Delegate a focused subtask to a fresh copy of yourself. Use it to isolate complex work or split a large task into independent pieces. Issue multiple `agent` calls in one response to run a small fixed set in parallel. Each child has fresh history and state but shares your tools and sandbox, so include essential context in `message` and give parallel writers non-overlapping scopes.\",\"name\":\"agent\",\"parameters\":{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\",\"description\":\"The message to send to the subagent. Provide all context the subagent needs to complete the task; the subagent does not see the parent's history.\"},\"outputSchema\":{\"type\":\"object\",\"description\":\"When provided, the subagent runs in task mode and must produce structured output matching this JSON Schema. The structured output becomes the tool result.\"}},\"required\":[\"message\"],\"additionalProperties\":false},\"strict\":false},{\"type\":\"web_search\",\"return_token_budget\":\"default\",\"search_content_types\":[\"text\"],\"search_context_size\":\"medium\",\"user_location\":{\"type\":\"approximate\",\"city\":null,\"country\":\"US\",\"region\":null,\"timezone\":null}}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":7029,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":6528},\"output_tokens\":65,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":7094},\"user\":null,\"metadata\":{}},\"sequence_number\":68}" + ], + "kind": "sse" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f20bffd865b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:29 GMT", + "openai-processing-ms": "310", + "openai-version": "2020-10-01", + "server": "cloudflare", + "set-cookie": "[REDACTED]", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "transfer-encoding": "chunked", + "x-content-type-options": "nosniff", + "x-ratelimit-limit-requests": "30000", + "x-ratelimit-limit-tokens": "180000000", + "x-ratelimit-remaining-requests": "29999", + "x-ratelimit-remaining-tokens": "179992329", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "req_724b88010ec441a984beca6be1ca998a" }, "status": 200, "statusText": "OK" diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json index 8f065a81c..e45ffe2c7 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -1,190 +1,233 @@ { "span_tree": [ { - "name": "eve.turn", + "name": "eve.session", "type": "task", "children": [ { - "name": "eve.step", - "type": "llm", - "children": [], - "input": [ - { - "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "role": "system" - }, - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ], - "output": [ + "name": "eve.turn", + "type": "task", + "children": [ { - "finish_reason": "tool_calls", - "index": 0, - "message": { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "researcher" - }, - "id": "", - "type": "function" + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "researcher" + }, + "id": "", + "type": "function" + } + ] } - ] + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 28, + "prompt_cached_tokens": 0, + "prompt_tokens": 6666, + "tokens": 6694 } - } - ], - "metadata": { - "model": "gpt-5.4-mini", - "provider": "openai", - "scenario": "eve-instrumentation", - "testRunId": "" - }, - "metrics": { - "completion_tokens": 28, - "prompt_cached_tokens": 0, - "prompt_tokens": 6666, - "tokens": 6694 - } - }, - { - "name": "researcher", - "type": "tool", - "children": [ + }, { - "name": "eve.turn", - "type": "task", + "name": "researcher", + "type": "tool", "children": [ { - "name": "eve.step", - "type": "llm", - "children": [], - "input": [ - { - "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "role": "system" - }, + "name": "eve.session", + "type": "task", + "children": [ { - "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ], - "output": [ - { - "finish_reason": "tool_calls", - "index": 0, - "message": { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - "name": "search" + "name": "eve.turn", + "type": "task", + "children": [ + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" }, - "id": "", - "type": "function" + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 26, + "prompt_cached_tokens": 0, + "prompt_tokens": 6554, + "tokens": 6580 } - ] - } - } - ], - "metadata": { - "model": "gpt-5.4-mini", - "provider": "openai", - "scenario": "eve-instrumentation", - "testRunId": "" - }, - "metrics": { - "completion_tokens": 26, - "prompt_cached_tokens": 5504, - "prompt_tokens": 6554, - "tokens": 6580 - } - }, - { - "name": "search", - "type": "tool", - "children": [], - "input": { - "query": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "output": { - "query": "Run the Braintrust Eve instrumentation e2e scenario", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "metadata": { - "scenario": "eve-instrumentation", - "testRunId": "" - } - }, - { - "name": "eve.step", - "type": "llm", - "children": [], - "input": [ - { - "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "role": "system" - }, - { - "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, - { - "content": [ + }, { + "name": "search", + "type": "tool", + "children": [], "input": { "query": "Run the Braintrust Eve instrumentation e2e scenario" }, - "providerOptions": { - "openai": { - "itemId": "" - } + "output": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" }, - "toolCallId": "", - "toolName": "search", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + } + }, { - "output": { - "type": "json", - "value": { - "query": "Run the Braintrust Eve instrumentation e2e scenario", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "query": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "query": "Run the Braintrust Eve instrumentation e2e scenario", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-result" + } + ], + "role": "tool" } - }, - "providerOptions": { - "openai": { - "itemId": "" + ], + "output": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "role": "assistant" + } } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" }, - "toolCallId": "", - "toolName": "search", - "type": "tool-result" + "metrics": { + "completion_tokens": 22, + "prompt_cached_tokens": 5504, + "prompt_tokens": 6624, + "tokens": 6646 + } } ], - "role": "tool" - } - ], - "output": [ - { - "finish_reason": "stop", - "index": 0, - "message": { - "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", - "role": "assistant" + "input": [ + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "role": "user" + } + ], + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 48, + "prompt_cached_tokens": 5504, + "prompt_tokens": 13178, + "tokens": 13226 } } ], @@ -193,22 +236,84 @@ "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" - }, - "metrics": { - "completion_tokens": 22, - "prompt_cached_tokens": 6528, - "prompt_tokens": 6624, - "tokens": 6646 } } ], + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + } + }, + { + "name": "eve.step", + "type": "llm", + "children": [], "input": [ { - "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + } + ], + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "name": "read" + }, + "id": "", + "type": "function" + } + ] + } } ], - "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "metadata": { "model": "gpt-5.4-mini", "provider": "openai", @@ -216,88 +321,149 @@ "testRunId": "" }, "metrics": { - "completion_tokens": 48, - "prompt_cached_tokens": 12032, - "prompt_tokens": 13178, - "tokens": 13226 + "completion_tokens": 27, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6724, + "tokens": 6751 } - } - ], - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", - "metadata": { - "scenario": "eve-instrumentation", - "testRunId": "" - } - }, - { - "name": "eve.step", - "type": "llm", - "children": [], - "input": [ - { - "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "role": "system" }, { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" + "name": "read", + "type": "tool", + "children": [], + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "output": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + } }, { - "content": [ - { - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "providerOptions": { - "openai": { - "itemId": "" + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" } - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-call" + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" } ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "text", - "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-result" + "output": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "role": "assistant" + } } ], - "role": "tool" + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 65, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6806, + "tokens": 6871 + } } ], - "output": [ + "input": [ { - "finish_reason": "tool_calls", - "index": 0, - "message": { - "content": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - "name": "read" - }, - "id": "", - "type": "function" - } - ] - } + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" } ], + "output": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", "metadata": { "model": "gpt-5.4-mini", "provider": "openai", @@ -305,128 +471,751 @@ "testRunId": "" }, "metrics": { - "completion_tokens": 27, - "prompt_cached_tokens": 6528, - "prompt_tokens": 6724, - "tokens": 6751 + "completion_tokens": 120, + "prompt_cached_tokens": 13056, + "prompt_tokens": 20196, + "tokens": 20316 } }, { - "name": "read", - "type": "tool", - "children": [], - "input": { - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "output": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - "section": "Runtime context", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "metadata": { - "scenario": "eve-instrumentation", - "testRunId": "" - } - }, - { - "name": "eve.step", - "type": "llm", - "children": [], - "input": [ - { - "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "role": "system" - }, - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - }, + "name": "eve.turn", + "type": "task", + "children": [ { - "content": [ - { - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "providerOptions": { - "openai": { - "itemId": "" + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "providerOptions": { + "openai": { + "itemId": "", + "phase": "final_answer" + } + }, + "text": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "type": "text" } - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-call" + ], + "role": "assistant" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario again", + "role": "user" } ], - "role": "assistant" + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario again\"}", + "name": "researcher" + }, + "id": "", + "type": "function" + } + ] + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 29, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6888, + "tokens": 6917 + } }, { - "content": [ - { - "output": { - "type": "text", - "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-result" + "name": "researcher", + "type": "tool", + "children": [ + { + "name": "eve.session", + "type": "task", + "children": [ + { + "name": "eve.turn", + "type": "task", + "children": [ + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + "role": "user" + } + ], + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario again\"}", + "name": "search" + }, + "id": "", + "type": "function" + } + ] + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 27, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6555, + "tokens": 6582 + } + }, + { + "name": "search", + "type": "tool", + "children": [], + "input": { + "query": "Run the Braintrust Eve instrumentation e2e scenario again" + }, + "output": { + "query": "Run the Braintrust Eve instrumentation e2e scenario again", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + } + }, + { + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + "role": "user" + }, + { + "content": [ + { + "input": { + "query": "Run the Braintrust Eve instrumentation e2e scenario again" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "query": "Run the Braintrust Eve instrumentation e2e scenario again", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "search", + "type": "tool-result" + } + ], + "role": "tool" + } + ], + "output": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "role": "assistant" + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 22, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6627, + "tokens": 6649 + } + } + ], + "input": [ + { + "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + "role": "user" + } + ], + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 49, + "prompt_cached_tokens": 13056, + "prompt_tokens": 13182, + "tokens": 13231 + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + } } ], - "role": "tool" + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario again" + }, + "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + } }, { - "content": [ - { - "input": { - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "providerOptions": { - "openai": { - "itemId": "" + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-call" + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "providerOptions": { + "openai": { + "itemId": "", + "phase": "final_answer" + } + }, + "text": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "type": "text" + } + ], + "role": "assistant" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario again", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario again" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + } + ], + "output": [ + { + "finish_reason": "tool_calls", + "index": 0, + "message": { + "content": null, + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + "name": "read" + }, + "id": "", + "type": "function" + } + ] + } } ], - "role": "assistant" + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 27, + "prompt_cached_tokens": 6528, + "prompt_tokens": 6947, + "tokens": 6974 + } + }, + { + "name": "read", + "type": "tool", + "children": [], + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "output": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "metadata": { + "scenario": "eve-instrumentation", + "testRunId": "" + } }, { - "content": [ - { - "output": { - "type": "json", - "value": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - "section": "Runtime context", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" + "name": "eve.step", + "type": "llm", + "children": [], + "input": [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "providerOptions": { + "openai": { + "itemId": "", + "phase": "final_answer" + } + }, + "text": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "type": "text" + } + ], + "role": "assistant" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario again", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario again" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" } - }, - "providerOptions": { - "openai": { - "itemId": "" + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-result" + ], + "role": "tool" } ], - "role": "tool" + "output": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "role": "assistant" + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 65, + "prompt_cached_tokens": 6528, + "prompt_tokens": 7029, + "tokens": 7094 + } } ], - "output": [ + "input": [ { - "finish_reason": "stop", - "index": 0, - "message": { - "content": "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - "role": "assistant" - } + "content": "Run the Braintrust Eve instrumentation e2e scenario again", + "role": "user" } ], + "output": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", "metadata": { "model": "gpt-5.4-mini", "provider": "openai", @@ -434,31 +1223,18 @@ "testRunId": "" }, "metrics": { - "completion_tokens": 66, - "prompt_cached_tokens": 6528, - "prompt_tokens": 6806, - "tokens": 6872 + "completion_tokens": 121, + "prompt_cached_tokens": 19584, + "prompt_tokens": 20864, + "tokens": 20985 } } ], - "input": [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ], - "output": "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", "metadata": { "model": "gpt-5.4-mini", "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" - }, - "metrics": { - "completion_tokens": 121, - "prompt_cached_tokens": 13056, - "prompt_tokens": 20196, - "tokens": 20317 } } ] diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt index f82c3aea3..e135796ba 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -1,55 +1,19 @@ span_tree: -└── eve.turn [task] - input: [ - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", - "role": "user" - } - ] - output: "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace." +└── eve.session [task] metadata: { "model": "gpt-5.4-mini", "provider": "openai", "scenario": "eve-instrumentation", "testRunId": "" } - metrics: { - "completion_tokens": 121, - "prompt_cached_tokens": 13056, - "prompt_tokens": 20196, - "tokens": 20317 - } - ├── eve.step [llm] + ├── eve.turn [task] │ input: [ │ { - │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ "role": "system" - │ }, - │ { │ "content": "Run the Braintrust Eve instrumentation e2e scenario", │ "role": "user" │ } │ ] - │ output: [ - │ { - │ "finish_reason": "tool_calls", - │ "index": 0, - │ "message": { - │ "content": null, - │ "role": "assistant", - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - │ "name": "researcher" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] - │ } - │ } - │ ] + │ output: "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace." │ metadata: { │ "model": "gpt-5.4-mini", │ "provider": "openai", @@ -57,354 +21,424 @@ span_tree: │ "testRunId": "" │ } │ metrics: { - │ "completion_tokens": 28, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 6666, - │ "tokens": 6694 - │ } - ├── researcher [tool] - │ input: { - │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ "completion_tokens": 120, + │ "prompt_cached_tokens": 13056, + │ "prompt_tokens": 20196, + │ "tokens": 20316 │ } - │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - │ metadata: { - │ "scenario": "eve-instrumentation", - │ "testRunId": "" - │ } - │ └── eve.turn [task] + │ ├── eve.step [llm] + │ │ input: [ + │ │ { + │ │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ │ "role": "system" + │ │ }, + │ │ { + │ │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ │ "role": "user" + │ │ } + │ │ ] + │ │ output: [ + │ │ { + │ │ "finish_reason": "tool_calls", + │ │ "index": 0, + │ │ "message": { + │ │ "content": null, + │ │ "role": "assistant", + │ │ "tool_calls": [ + │ │ { + │ │ "function": { + │ │ "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ │ "name": "researcher" + │ │ }, + │ │ "id": "", + │ │ "type": "function" + │ │ } + │ │ ] + │ │ } + │ │ } + │ │ ] + │ │ metadata: { + │ │ "model": "gpt-5.4-mini", + │ │ "provider": "openai", + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ metrics: { + │ │ "completion_tokens": 28, + │ │ "prompt_cached_tokens": 0, + │ │ "prompt_tokens": 6666, + │ │ "tokens": 6694 + │ │ } + │ ├── researcher [tool] + │ │ input: { + │ │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ │ } + │ │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ │ metadata: { + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ └── eve.session [task] + │ │ metadata: { + │ │ "model": "gpt-5.4-mini", + │ │ "provider": "openai", + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ └── eve.turn [task] + │ │ input: [ + │ │ { + │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ │ "role": "user" + │ │ } + │ │ ] + │ │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ │ metadata: { + │ │ "model": "gpt-5.4-mini", + │ │ "provider": "openai", + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ metrics: { + │ │ "completion_tokens": 48, + │ │ "prompt_cached_tokens": 5504, + │ │ "prompt_tokens": 13178, + │ │ "tokens": 13226 + │ │ } + │ │ ├── eve.step [llm] + │ │ │ input: [ + │ │ │ { + │ │ │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ │ │ "role": "system" + │ │ │ }, + │ │ │ { + │ │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ │ │ "role": "user" + │ │ │ } + │ │ │ ] + │ │ │ output: [ + │ │ │ { + │ │ │ "finish_reason": "tool_calls", + │ │ │ "index": 0, + │ │ │ "message": { + │ │ │ "content": null, + │ │ │ "role": "assistant", + │ │ │ "tool_calls": [ + │ │ │ { + │ │ │ "function": { + │ │ │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", + │ │ │ "name": "search" + │ │ │ }, + │ │ │ "id": "", + │ │ │ "type": "function" + │ │ │ } + │ │ │ ] + │ │ │ } + │ │ │ } + │ │ │ ] + │ │ │ metadata: { + │ │ │ "model": "gpt-5.4-mini", + │ │ │ "provider": "openai", + │ │ │ "scenario": "eve-instrumentation", + │ │ │ "testRunId": "" + │ │ │ } + │ │ │ metrics: { + │ │ │ "completion_tokens": 26, + │ │ │ "prompt_cached_tokens": 0, + │ │ │ "prompt_tokens": 6554, + │ │ │ "tokens": 6580 + │ │ │ } + │ │ ├── search [tool] + │ │ │ input: { + │ │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario" + │ │ │ } + │ │ │ output: { + │ │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario", + │ │ │ "title": "Eve instrumentation", + │ │ │ "url": "https://eve.dev/docs/guides/instrumentation" + │ │ │ } + │ │ │ metadata: { + │ │ │ "scenario": "eve-instrumentation", + │ │ │ "testRunId": "" + │ │ │ } + │ │ └── eve.step [llm] + │ │ input: [ + │ │ { + │ │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ │ "role": "system" + │ │ }, + │ │ { + │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ │ "role": "user" + │ │ }, + │ │ { + │ │ "content": [ + │ │ { + │ │ "input": { + │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario" + │ │ }, + │ │ "providerOptions": { + │ │ "openai": { + │ │ "itemId": "" + │ │ } + │ │ }, + │ │ "toolCallId": "", + │ │ "toolName": "search", + │ │ "type": "tool-call" + │ │ } + │ │ ], + │ │ "role": "assistant" + │ │ }, + │ │ { + │ │ "content": [ + │ │ { + │ │ "output": { + │ │ "type": "json", + │ │ "value": { + │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario", + │ │ "title": "Eve instrumentation", + │ │ "url": "https://eve.dev/docs/guides/instrumentation" + │ │ } + │ │ }, + │ │ "providerOptions": { + │ │ "openai": { + │ │ "itemId": "" + │ │ } + │ │ }, + │ │ "toolCallId": "", + │ │ "toolName": "search", + │ │ "type": "tool-result" + │ │ } + │ │ ], + │ │ "role": "tool" + │ │ } + │ │ ] + │ │ output: [ + │ │ { + │ │ "finish_reason": "stop", + │ │ "index": 0, + │ │ "message": { + │ │ "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + │ │ "role": "assistant" + │ │ } + │ │ } + │ │ ] + │ │ metadata: { + │ │ "model": "gpt-5.4-mini", + │ │ "provider": "openai", + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ metrics: { + │ │ "completion_tokens": 22, + │ │ "prompt_cached_tokens": 5504, + │ │ "prompt_tokens": 6624, + │ │ "tokens": 6646 + │ │ } + │ ├── eve.step [llm] + │ │ input: [ + │ │ { + │ │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ │ "role": "system" + │ │ }, + │ │ { + │ │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ │ "role": "user" + │ │ }, + │ │ { + │ │ "content": [ + │ │ { + │ │ "input": { + │ │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ │ }, + │ │ "providerOptions": { + │ │ "openai": { + │ │ "itemId": "" + │ │ } + │ │ }, + │ │ "toolCallId": "", + │ │ "toolName": "researcher", + │ │ "type": "tool-call" + │ │ } + │ │ ], + │ │ "role": "assistant" + │ │ }, + │ │ { + │ │ "content": [ + │ │ { + │ │ "output": { + │ │ "type": "text", + │ │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ │ }, + │ │ "toolCallId": "", + │ │ "toolName": "researcher", + │ │ "type": "tool-result" + │ │ } + │ │ ], + │ │ "role": "tool" + │ │ } + │ │ ] + │ │ output: [ + │ │ { + │ │ "finish_reason": "tool_calls", + │ │ "index": 0, + │ │ "message": { + │ │ "content": null, + │ │ "role": "assistant", + │ │ "tool_calls": [ + │ │ { + │ │ "function": { + │ │ "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + │ │ "name": "read" + │ │ }, + │ │ "id": "", + │ │ "type": "function" + │ │ } + │ │ ] + │ │ } + │ │ } + │ │ ] + │ │ metadata: { + │ │ "model": "gpt-5.4-mini", + │ │ "provider": "openai", + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ metrics: { + │ │ "completion_tokens": 27, + │ │ "prompt_cached_tokens": 6528, + │ │ "prompt_tokens": 6724, + │ │ "tokens": 6751 + │ │ } + │ ├── read [tool] + │ │ input: { + │ │ "url": "https://eve.dev/docs/guides/instrumentation" + │ │ } + │ │ output: { + │ │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ │ "section": "Runtime context", + │ │ "title": "Eve instrumentation", + │ │ "url": "https://eve.dev/docs/guides/instrumentation" + │ │ } + │ │ metadata: { + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ └── eve.step [llm] │ input: [ │ { - │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", + │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "role": "system" + │ }, + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", │ "role": "user" - │ } - │ ] - │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - │ metadata: { - │ "model": "gpt-5.4-mini", - │ "provider": "openai", - │ "scenario": "eve-instrumentation", - │ "testRunId": "" - │ } - │ metrics: { - │ "completion_tokens": 48, - │ "prompt_cached_tokens": 12032, - │ "prompt_tokens": 13178, - │ "tokens": 13226 - │ } - │ ├── eve.step [llm] - │ │ input: [ - │ │ { - │ │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ │ "role": "system" - │ │ }, - │ │ { - │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - │ │ "role": "user" - │ │ } - │ │ ] - │ │ output: [ - │ │ { - │ │ "finish_reason": "tool_calls", - │ │ "index": 0, - │ │ "message": { - │ │ "content": null, - │ │ "role": "assistant", - │ │ "tool_calls": [ - │ │ { - │ │ "function": { - │ │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario\"}", - │ │ "name": "search" - │ │ }, - │ │ "id": "", - │ │ "type": "function" - │ │ } - │ │ ] - │ │ } - │ │ } - │ │ ] - │ │ metadata: { - │ │ "model": "gpt-5.4-mini", - │ │ "provider": "openai", - │ │ "scenario": "eve-instrumentation", - │ │ "testRunId": "" - │ │ } - │ │ metrics: { - │ │ "completion_tokens": 26, - │ │ "prompt_cached_tokens": 5504, - │ │ "prompt_tokens": 6554, - │ │ "tokens": 6580 - │ │ } - │ ├── search [tool] - │ │ input: { - │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario" - │ │ } - │ │ output: { - │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario", - │ │ "title": "Eve instrumentation", - │ │ "url": "https://eve.dev/docs/guides/instrumentation" - │ │ } - │ │ metadata: { - │ │ "scenario": "eve-instrumentation", - │ │ "testRunId": "" - │ │ } - │ └── eve.step [llm] - │ input: [ - │ { - │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ "role": "system" - │ }, - │ { - │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ }, + │ }, + │ { + │ "content": [ │ { - │ "content": [ - │ { - │ "input": { - │ "query": "Run the Braintrust Eve instrumentation e2e scenario" - │ }, - │ "providerOptions": { - │ "openai": { - │ "itemId": "" - │ } - │ }, - │ "toolCallId": "", - │ "toolName": "search", - │ "type": "tool-call" + │ "input": { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" │ } - │ ], - │ "role": "assistant" - │ }, + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ │ { - │ "content": [ - │ { - │ "output": { - │ "type": "json", - │ "value": { - │ "query": "Run the Braintrust Eve instrumentation e2e scenario", - │ "title": "Eve instrumentation", - │ "url": "https://eve.dev/docs/guides/instrumentation" - │ } - │ }, - │ "providerOptions": { - │ "openai": { - │ "itemId": "" - │ } - │ }, - │ "toolCallId": "", - │ "toolName": "search", - │ "type": "tool-result" - │ } - │ ], - │ "role": "tool" + │ "output": { + │ "type": "text", + │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-result" │ } - │ ] - │ output: [ + │ ], + │ "role": "tool" + │ }, + │ { + │ "content": [ │ { - │ "finish_reason": "stop", - │ "index": 0, - │ "message": { - │ "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", - │ "role": "assistant" - │ } + │ "input": { + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "read", + │ "type": "tool-call" │ } - │ ] - │ metadata: { - │ "model": "gpt-5.4-mini", - │ "provider": "openai", - │ "scenario": "eve-instrumentation", - │ "testRunId": "" - │ } - │ metrics: { - │ "completion_tokens": 22, - │ "prompt_cached_tokens": 6528, - │ "prompt_tokens": 6624, - │ "tokens": 6646 - │ } - ├── eve.step [llm] - │ input: [ - │ { - │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - │ "role": "system" - │ }, - │ { - │ "content": "Run the Braintrust Eve instrumentation e2e scenario", - │ "role": "user" - │ }, - │ { - │ "content": [ + │ ], + │ "role": "assistant" + │ }, │ { - │ "input": { - │ "message": "Run the Braintrust Eve instrumentation e2e scenario" - │ }, - │ "providerOptions": { - │ "openai": { - │ "itemId": "" + │ "content": [ + │ { + │ "output": { + │ "type": "json", + │ "value": { + │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ "section": "Runtime context", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "read", + │ "type": "tool-result" │ } - │ }, - │ "toolCallId": "", - │ "toolName": "researcher", - │ "type": "tool-call" + │ ], + │ "role": "tool" │ } - │ ], - │ "role": "assistant" - │ }, - │ { - │ "content": [ + │ ] + │ output: [ │ { - │ "output": { - │ "type": "text", - │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - │ }, - │ "toolCallId": "", - │ "toolName": "researcher", - │ "type": "tool-result" - │ } - │ ], - │ "role": "tool" - │ } - │ ] - │ output: [ - │ { - │ "finish_reason": "tool_calls", - │ "index": 0, - │ "message": { - │ "content": null, - │ "role": "assistant", - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", - │ "name": "read" - │ }, - │ "id": "", - │ "type": "function" + │ "finish_reason": "stop", + │ "index": 0, + │ "message": { + │ "content": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ "role": "assistant" │ } - │ ] + │ } + │ ] + │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" │ } - │ } - │ ] - │ metadata: { - │ "model": "gpt-5.4-mini", - │ "provider": "openai", - │ "scenario": "eve-instrumentation", - │ "testRunId": "" - │ } - │ metrics: { - │ "completion_tokens": 27, - │ "prompt_cached_tokens": 6528, - │ "prompt_tokens": 6724, - │ "tokens": 6751 - │ } - ├── read [tool] - │ input: { - │ "url": "https://eve.dev/docs/guides/instrumentation" - │ } - │ output: { - │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - │ "section": "Runtime context", - │ "title": "Eve instrumentation", - │ "url": "https://eve.dev/docs/guides/instrumentation" - │ } - │ metadata: { - │ "scenario": "eve-instrumentation", - │ "testRunId": "" - │ } - └── eve.step [llm] + │ metrics: { + │ "completion_tokens": 65, + │ "prompt_cached_tokens": 6528, + │ "prompt_tokens": 6806, + │ "tokens": 6871 + │ } + └── eve.turn [task] input: [ { - "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", - "role": "system" - }, - { - "content": "Run the Braintrust Eve instrumentation e2e scenario", + "content": "Run the Braintrust Eve instrumentation e2e scenario again", "role": "user" - }, - { - "content": [ - { - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "text", - "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" - }, - "toolCallId": "", - "toolName": "researcher", - "type": "tool-result" - } - ], - "role": "tool" - }, - { - "content": [ - { - "input": { - "url": "https://eve.dev/docs/guides/instrumentation" - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-call" - } - ], - "role": "assistant" - }, - { - "content": [ - { - "output": { - "type": "json", - "value": { - "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - "section": "Runtime context", - "title": "Eve instrumentation", - "url": "https://eve.dev/docs/guides/instrumentation" - } - }, - "providerOptions": { - "openai": { - "itemId": "" - } - }, - "toolCallId": "", - "toolName": "read", - "type": "tool-result" - } - ], - "role": "tool" - } - ] - output: [ - { - "finish_reason": "stop", - "index": 0, - "message": { - "content": "Final answer from read: researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title: Eve instrumentation; URL: https://eve.dev/docs/guides/instrumentation; read excerpt: Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", - "role": "assistant" - } } ] + output: "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace." metadata: { "model": "gpt-5.4-mini", "provider": "openai", @@ -412,8 +446,692 @@ span_tree: "testRunId": "" } metrics: { - "completion_tokens": 66, - "prompt_cached_tokens": 6528, - "prompt_tokens": 6806, - "tokens": 6872 + "completion_tokens": 121, + "prompt_cached_tokens": 19584, + "prompt_tokens": 20864, + "tokens": 20985 } + ├── eve.step [llm] + │ input: [ + │ { + │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "role": "system" + │ }, + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "text", + │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "read", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "json", + │ "value": { + │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ "section": "Runtime context", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "read", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ }, + │ { + │ "content": [ + │ { + │ "providerOptions": { + │ "openai": { + │ "itemId": "", + │ "phase": "final_answer" + │ } + │ }, + │ "text": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ "type": "text" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario again", + │ "role": "user" + │ } + │ ] + │ output: [ + │ { + │ "finish_reason": "tool_calls", + │ "index": 0, + │ "message": { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"message\":\"Run the Braintrust Eve instrumentation e2e scenario again\"}", + │ "name": "researcher" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ } + │ } + │ ] + │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ metrics: { + │ "completion_tokens": 29, + │ "prompt_cached_tokens": 6528, + │ "prompt_tokens": 6888, + │ "tokens": 6917 + │ } + ├── researcher [tool] + │ input: { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario again" + │ } + │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ metadata: { + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ └── eve.session [task] + │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ └── eve.turn [task] + │ input: [ + │ { + │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + │ "role": "user" + │ } + │ ] + │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ metrics: { + │ "completion_tokens": 49, + │ "prompt_cached_tokens": 13056, + │ "prompt_tokens": 13182, + │ "tokens": 13231 + │ } + │ ├── eve.step [llm] + │ │ input: [ + │ │ { + │ │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ │ "role": "system" + │ │ }, + │ │ { + │ │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + │ │ "role": "user" + │ │ } + │ │ ] + │ │ output: [ + │ │ { + │ │ "finish_reason": "tool_calls", + │ │ "index": 0, + │ │ "message": { + │ │ "content": null, + │ │ "role": "assistant", + │ │ "tool_calls": [ + │ │ { + │ │ "function": { + │ │ "arguments": "{\"query\":\"Run the Braintrust Eve instrumentation e2e scenario again\"}", + │ │ "name": "search" + │ │ }, + │ │ "id": "", + │ │ "type": "function" + │ │ } + │ │ ] + │ │ } + │ │ } + │ │ ] + │ │ metadata: { + │ │ "model": "gpt-5.4-mini", + │ │ "provider": "openai", + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ │ metrics: { + │ │ "completion_tokens": 27, + │ │ "prompt_cached_tokens": 6528, + │ │ "prompt_tokens": 6555, + │ │ "tokens": 6582 + │ │ } + │ ├── search [tool] + │ │ input: { + │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario again" + │ │ } + │ │ output: { + │ │ "query": "Run the Braintrust Eve instrumentation e2e scenario again", + │ │ "title": "Eve instrumentation", + │ │ "url": "https://eve.dev/docs/guides/instrumentation" + │ │ } + │ │ metadata: { + │ │ "scenario": "eve-instrumentation", + │ │ "testRunId": "" + │ │ } + │ └── eve.step [llm] + │ input: [ + │ { + │ "content": "Instructions (instructions)\nYou are the deterministic researcher subagent for Braintrust Eve instrumentation tests.\n\nFor every task:\n\n1. Call the search tool exactly once with the full task as the query.\n2. After the search result is available, answer with a single sentence that starts\n with \"Researcher result:\" and includes the search title and URL.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "role": "system" + │ }, + │ { + │ "content": "You are the subagent \"researcher\".\nDescription: Research the Eve instrumentation documentation before the parent reads it.\n\nThe caller delegated the following task to you. Complete it and return the final result directly.\n\nCaller message:\nRun the Braintrust Eve instrumentation e2e scenario again", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario again" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "search", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "json", + │ "value": { + │ "query": "Run the Braintrust Eve instrumentation e2e scenario again", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "search", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ } + │ ] + │ output: [ + │ { + │ "finish_reason": "stop", + │ "index": 0, + │ "message": { + │ "content": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", + │ "role": "assistant" + │ } + │ } + │ ] + │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ metrics: { + │ "completion_tokens": 22, + │ "prompt_cached_tokens": 6528, + │ "prompt_tokens": 6627, + │ "tokens": 6649 + │ } + ├── eve.step [llm] + │ input: [ + │ { + │ "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + │ "role": "system" + │ }, + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "text", + │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "read", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "json", + │ "value": { + │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ "section": "Runtime context", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "read", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ }, + │ { + │ "content": [ + │ { + │ "providerOptions": { + │ "openai": { + │ "itemId": "", + │ "phase": "final_answer" + │ } + │ }, + │ "text": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ "type": "text" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": "Run the Braintrust Eve instrumentation e2e scenario again", + │ "role": "user" + │ }, + │ { + │ "content": [ + │ { + │ "input": { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario again" + │ }, + │ "providerOptions": { + │ "openai": { + │ "itemId": "" + │ } + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-call" + │ } + │ ], + │ "role": "assistant" + │ }, + │ { + │ "content": [ + │ { + │ "output": { + │ "type": "text", + │ "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + │ }, + │ "toolCallId": "", + │ "toolName": "researcher", + │ "type": "tool-result" + │ } + │ ], + │ "role": "tool" + │ } + │ ] + │ output: [ + │ { + │ "finish_reason": "tool_calls", + │ "index": 0, + │ "message": { + │ "content": null, + │ "role": "assistant", + │ "tool_calls": [ + │ { + │ "function": { + │ "arguments": "{\"url\":\"https://eve.dev/docs/guides/instrumentation\"}", + │ "name": "read" + │ }, + │ "id": "", + │ "type": "function" + │ } + │ ] + │ } + │ } + │ ] + │ metadata: { + │ "model": "gpt-5.4-mini", + │ "provider": "openai", + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + │ metrics: { + │ "completion_tokens": 27, + │ "prompt_cached_tokens": 6528, + │ "prompt_tokens": 6947, + │ "tokens": 6974 + │ } + ├── read [tool] + │ input: { + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ output: { + │ "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + │ "section": "Runtime context", + │ "title": "Eve instrumentation", + │ "url": "https://eve.dev/docs/guides/instrumentation" + │ } + │ metadata: { + │ "scenario": "eve-instrumentation", + │ "testRunId": "" + │ } + └── eve.step [llm] + input: [ + { + "content": "Instructions (instructions)\nYou are a deterministic fixture agent for Braintrust Eve instrumentation tests.\n\nFor every user task:\n\n1. Call the researcher subagent exactly once with the full user message as the\n message.\n2. After the researcher result is available, call the read tool exactly once with\n the URL https://eve.dev/docs/guides/instrumentation.\n3. After the read result is available, answer with a single sentence that starts\n with \"Final answer from read:\" and includes the researcher result, read title,\n URL, and read excerpt.\n\nTool execution\nA single tool or subagent call runs as one serial action. If you call multiple independent tools or subagents in one response, eve treats that batch as parallel work. Only batch work that is independent and does not rely on another call in the same response.", + "role": "system" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "providerOptions": { + "openai": { + "itemId": "", + "phase": "final_answer" + } + }, + "text": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "type": "text" + } + ], + "role": "assistant" + }, + { + "content": "Run the Braintrust Eve instrumentation e2e scenario again", + "role": "user" + }, + { + "content": [ + { + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario again" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "text", + "value": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" + }, + "toolCallId": "", + "toolName": "researcher", + "type": "tool-result" + } + ], + "role": "tool" + }, + { + "content": [ + { + "input": { + "url": "https://eve.dev/docs/guides/instrumentation" + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-call" + } + ], + "role": "assistant" + }, + { + "content": [ + { + "output": { + "type": "json", + "value": { + "excerpt": "Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "section": "Runtime context", + "title": "Eve instrumentation", + "url": "https://eve.dev/docs/guides/instrumentation" + } + }, + "providerOptions": { + "openai": { + "itemId": "" + } + }, + "toolCallId": "", + "toolName": "read", + "type": "tool-result" + } + ], + "role": "tool" + } + ] + output: [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "content": "Final answer from read: researcher result = Eve instrumentation — https://eve.dev/docs/guides/instrumentation; read title = Eve instrumentation; URL = https://eve.dev/docs/guides/instrumentation; excerpt = Eve hooks expose runtime stream events that Braintrust maps into a nested turn trace.", + "role": "assistant" + } + } + ] + metadata: { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + } + metrics: { + "completion_tokens": 65, + "prompt_cached_tokens": 6528, + "prompt_tokens": 7029, + "tokens": 7094 + } diff --git a/e2e/scenarios/eve-instrumentation/scenario.test.ts b/e2e/scenarios/eve-instrumentation/scenario.test.ts index 25e38d332..04cfe0d99 100644 --- a/e2e/scenarios/eve-instrumentation/scenario.test.ts +++ b/e2e/scenarios/eve-instrumentation/scenario.test.ts @@ -8,9 +8,9 @@ import { } from "../../helpers/scenario-harness"; import { matchSpanTreeSnapshot } from "../../helpers/span-tree"; import { + findAllSpans, findChildSpans, findLatestChildSpan, - findLatestSpan, } from "../../helpers/trace-selectors"; const originalScenarioDir = resolveScenarioDir(import.meta.url); @@ -46,14 +46,23 @@ describe("eve instrumentation", () => { ); }, TIMEOUT_MS); - test("captures a nested Eve local subagent trace", async () => { - const root = findLatestSpan(events, "eve.turn"); + test("captures multiple nested turns in one Eve session", async () => { + const session = findAllSpans(events, "eve.session").find( + (span) => span.span.parentIds.length === 0, + ); + const turns = findChildSpans(events, "eve.turn", session?.span.id); + const [root, secondRoot] = turns; const steps = findChildSpans(events, "eve.step", root?.span.id); const researcher = findChildSpans(events, "researcher", root?.span.id)[0]; + const childSession = findChildSpans( + events, + "eve.session", + researcher?.span.id, + )[0]; const childTurn = findChildSpans( events, "eve.turn", - researcher?.span.id, + childSession?.span.id, )[0]; const childSteps = findChildSpans(events, "eve.step", childTurn?.span.id); const childSearch = findLatestChildSpan( @@ -62,10 +71,56 @@ describe("eve instrumentation", () => { childTurn?.span.id, ); const read = findLatestChildSpan(events, "read", root?.span.id); + const secondSteps = findChildSpans(events, "eve.step", secondRoot?.span.id); + const secondResearcher = findLatestChildSpan( + events, + "researcher", + secondRoot?.span.id, + ); + const secondChildSession = findLatestChildSpan( + events, + "eve.session", + secondResearcher?.span.id, + ); + const secondChildTurn = findLatestChildSpan( + events, + "eve.turn", + secondChildSession?.span.id, + ); + const secondRead = findLatestChildSpan(events, "read", secondRoot?.span.id); + + expect(session).toBeDefined(); + expect(session?.span.type).toBe("task"); + expect(session?.span.parentIds).toEqual([]); + expect(session?.metadata).toMatchObject({ + model: "gpt-5.4-mini", + provider: "openai", + scenario: "eve-instrumentation", + testRunId: expect.any(String), + }); + expect(turns).toHaveLength(2); + expect(turns.map((turn) => turn.span.parentIds)).toEqual([ + [session?.span.id], + [session?.span.id], + ]); + expect(turns.map((turn) => turn.input)).toEqual([ + [ + { + content: "Run the Braintrust Eve instrumentation e2e scenario", + role: "user", + }, + ], + [ + { + content: "Run the Braintrust Eve instrumentation e2e scenario again", + role: "user", + }, + ], + ]); expect(root).toBeDefined(); expect(root?.span.type).toBe("task"); - expect(root?.span.parentIds).toEqual([]); + expect(root?.span.parentIds).toEqual([session?.span.id]); expect(root?.metadata).toMatchObject({ scenario: "eve-instrumentation", testRunId: expect.any(String), @@ -105,8 +160,12 @@ describe("eve instrumentation", () => { }); expect(researcher?.output).toContain("Researcher result"); + expect(childSession).toBeDefined(); + expect(childSession?.span.parentIds).toEqual([researcher?.span.id]); + expect(childSession?.span.rootId).toEqual(session?.span.rootId); + expect(childTurn).toBeDefined(); - expect(childTurn?.span.parentIds).toEqual([researcher?.span.id]); + expect(childTurn?.span.parentIds).toEqual([childSession?.span.id]); expect(childTurn?.span.rootId).toEqual(root?.span.rootId); expect(childTurn?.metadata).toMatchObject({ scenario: "eve-instrumentation", @@ -153,6 +212,28 @@ describe("eve instrumentation", () => { title: "Eve instrumentation", }); + expect(secondRoot).toBeDefined(); + expect(secondRoot?.span.type).toBe("task"); + expect(secondRoot?.output).toContain("Final answer from read"); + expect(secondSteps).toHaveLength(3); + expect(secondSteps.map((step) => step.span.type)).toEqual([ + "llm", + "llm", + "llm", + ]); + expect(secondResearcher?.span.type).toBe("tool"); + expect(secondResearcher?.span.ended).toBe(true); + expect(secondResearcher?.span.parentIds).toEqual([secondRoot?.span.id]); + expect(secondChildSession?.span.parentIds).toEqual([ + secondResearcher?.span.id, + ]); + expect(secondChildTurn?.span.parentIds).toEqual([ + secondChildSession?.span.id, + ]); + expect(secondRead?.span.type).toBe("tool"); + expect(secondRead?.span.ended).toBe(true); + expect(secondRead?.span.parentIds).toEqual([secondRoot?.span.id]); + await matchSpanTreeSnapshot(events, spanTreeSnapshotPath); }); }); diff --git a/e2e/scenarios/eve-instrumentation/scenario.ts b/e2e/scenarios/eve-instrumentation/scenario.ts index 84a4bf0a8..1be778de1 100644 --- a/e2e/scenarios/eve-instrumentation/scenario.ts +++ b/e2e/scenarios/eve-instrumentation/scenario.ts @@ -43,15 +43,46 @@ async function main() { ); } - const body = (await response.json()) as { sessionId?: string }; - if (!body.sessionId) { - throw new Error(`Eve session create did not return a sessionId`); + const body = (await response.json()) as { + continuationToken?: string; + sessionId?: string; + }; + if (!body.sessionId || !body.continuationToken) { + throw new Error( + `Eve session create did not return a sessionId and continuationToken`, + ); } - await streamUntilTurnCompleted( + const seenSessionIds = new Set([body.sessionId]); + const nextIndex = await streamUntil( baseUrl, body.sessionId, - new Set([body.sessionId]), + seenSessionIds, + "session.waiting", + ); + const followUp = await fetch( + `${baseUrl}/eve/v1/session/${body.sessionId}`, + { + body: JSON.stringify({ + continuationToken: body.continuationToken, + message: "Run the Braintrust Eve instrumentation e2e scenario again", + }), + headers: { "content-type": "application/json" }, + method: "POST", + }, + ); + if (!followUp.ok) { + throw new Error( + `Eve session follow-up failed with ${followUp.status}: ${await followUp.text()}`, + ); + } + + await streamUntil( + baseUrl, + body.sessionId, + seenSessionIds, + "session.waiting", + nextIndex, ); await new Promise((resolve) => setTimeout(resolve, 5000)); } finally { @@ -136,15 +167,17 @@ async function waitForEve( throw new Error(`Timed out waiting for eve start\n${output()}`); } -async function streamUntilTurnCompleted( +async function streamUntil( baseUrl: string, sessionId: string, seenSessionIds: Set, -): Promise { + until: "session.waiting" | "turn.completed", + startIndex = 0, +): Promise { const controller = new AbortController(); const childStreams: Promise[] = []; const response = await fetch( - `${baseUrl}/eve/v1/session/${sessionId}/stream`, + `${baseUrl}/eve/v1/session/${sessionId}/stream?startIndex=${startIndex}`, { signal: controller.signal, }, @@ -158,11 +191,12 @@ async function streamUntilTurnCompleted( const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ""; + let nextIndex = startIndex; try { while (true) { const { done, value } = await reader.read(); if (done) { - throw new Error("Eve stream ended before turn.completed"); + throw new Error(`Eve stream ended before ${until}`); } buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); @@ -176,6 +210,7 @@ async function streamUntilTurnCompleted( data?: { childSessionId?: string; message?: string }; type?: string; }; + nextIndex++; if ( event.type === "step.failed" || event.type === "turn.failed" || @@ -192,16 +227,17 @@ async function streamUntilTurnCompleted( ) { seenSessionIds.add(event.data.childSessionId); childStreams.push( - streamUntilTurnCompleted( + streamUntil( baseUrl, event.data.childSessionId, seenSessionIds, - ), + "turn.completed", + ).then(() => undefined), ); } - if (event.type === "turn.completed") { + if (event.type === until) { await Promise.all(childStreams); - return; + return nextIndex; } } } diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index 3a12ef726..f102c6f0f 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -431,6 +431,9 @@ describe("braintrustEveHook", () => { const spans = (await backgroundLogger.drain()) as Array< Record >; + const session = spans.find( + (span) => span.span_attributes?.name === "eve.session", + ); const root = spans.find( (span) => span.span_attributes?.name === "eve.turn", ); @@ -440,11 +443,24 @@ describe("braintrustEveHook", () => { const tool = spans.find((span) => span.span_attributes?.name === "search"); expect(spans.map((span) => span.span_attributes?.name)).toEqual([ + "eve.session", "eve.turn", "eve.step", "search", "eve.step", ]); + expect(session).toMatchObject({ + metadata: { + ...expectedModelMetadata, + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", + }, + span_attributes: { + name: "eve.session", + type: "task", + }, + span_parents: [], + }); expect(root).toMatchObject({ input: [{ content: "Search then read", role: "user" }], metadata: { @@ -465,6 +481,7 @@ describe("braintrustEveHook", () => { name: "eve.turn", type: "task", }, + span_parents: [session?.span_id], }); expect(steps).toHaveLength(2); expect(steps.map((span) => span.span_attributes?.name)).toEqual([ @@ -531,6 +548,52 @@ describe("braintrustEveHook", () => { ]); }); + it("records each user message as a separate turn in one session", async () => { + const wildcard = braintrustEveHook().events?.["*"]; + expect(wildcard).toBeDefined(); + + const ctx: EveHookContext = { session: { id: "session-multi-turn" } }; + for (const [sequence, message] of [ + [0, "First user message"], + [1, "Second user message"], + ] as const) { + const turnId = `turn-${sequence}`; + await wildcard?.( + { data: { sequence, turnId }, type: "turn.started" }, + ctx, + ); + await wildcard?.( + { data: { message, sequence, turnId }, type: "message.received" }, + ctx, + ); + await wildcard?.( + { data: { sequence, turnId }, type: "turn.completed" }, + ctx, + ); + } + + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + const session = spans.find( + (span) => span.span_attributes?.name === "eve.session", + ); + const turns = spans.filter( + (span) => span.span_attributes?.name === "eve.turn", + ); + + expect(session?.span_parents).toEqual([]); + expect(turns).toHaveLength(2); + expect(turns.map((turn) => turn.span_parents)).toEqual([ + [session?.span_id], + [session?.span_id], + ]); + expect(turns.map((turn) => turn.input)).toEqual([ + [{ content: "First user message", role: "user" }], + [{ content: "Second user message", role: "user" }], + ]); + }); + it("does not reconstruct later LLM inputs from earlier hook events", async () => { const wildcard = braintrustEveHook().events?.["*"]; expect(wildcard).toBeDefined(); @@ -1187,11 +1250,19 @@ describe("braintrustEveHook", () => { "session-parent", "turn-parent", ); + const parentSessionId = deterministicEveIdForTest( + "eve:session", + "session-parent", + ); const childTurnId = deterministicEveIdForTest( "eve:turn", "session-child", "turn-child", ); + const childSessionId = deterministicEveIdForTest( + "eve:session", + "session-child", + ); const subagentSpanId = deterministicEveIdForTest( "eve:subagent", "session-parent", @@ -1202,6 +1273,11 @@ describe("braintrustEveHook", () => { span.span_attributes?.name === "eve.turn" && span.span_id === parentTurnId, ); + const parentSession = spans.find( + (span) => + span.span_attributes?.name === "eve.session" && + span.span_id === parentSessionId, + ); const subagentSpans = spans.filter( (span) => span.span_attributes?.name === "researcher", ); @@ -1210,6 +1286,11 @@ describe("braintrustEveHook", () => { span.span_attributes?.name === "eve.turn" && span.span_id === childTurnId, ); + const childSession = spans.find( + (span) => + span.span_attributes?.name === "eve.session" && + span.span_id === childSessionId, + ); const childSearch = spans.find( (span) => span.span_attributes?.name === "search" && @@ -1226,6 +1307,7 @@ describe("braintrustEveHook", () => { span.span_parents?.[0] === parentTurnId, ); + expect(parentSession).toBeDefined(); expect(parentTurn).toBeDefined(); expect(subagentSpans).toHaveLength(1); expect(subagentSpans[0]?.span_id).toBe(subagentSpanId); @@ -1233,7 +1315,8 @@ describe("braintrustEveHook", () => { expect(childSearch).toBeDefined(); expect(parentRead).toBeDefined(); expect(parentSteps).toHaveLength(3); - expect(parentTurn?.span_parents ?? []).toEqual([]); + expect(parentSession?.span_parents ?? []).toEqual([]); + expect(parentTurn?.span_parents).toEqual([parentSession?.span_id]); expect(parentTurn?.span_id).toBe(parentTurnId); expect(parentTurn?.span_id).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, @@ -1242,7 +1325,8 @@ describe("braintrustEveHook", () => { /^([0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/, ); expect(subagentSpans[0]?.span_parents).toEqual([parentTurn?.span_id]); - expect(childTurn?.span_parents).toEqual([subagentSpans[0]?.span_id]); + expect(childSession?.span_parents).toEqual([subagentSpans[0]?.span_id]); + expect(childTurn?.span_parents).toEqual([childSession?.span_id]); expect(childTurn?.root_span_id).toBe(parentTurn?.root_span_id); expect(childTurn?.metadata ?? {}).toEqual({}); expect(subagentSpans[0]?.metadata ?? {}).toEqual({}); @@ -1284,9 +1368,11 @@ describe("braintrustEveHook", () => { ), ]); expect(spans.map((span) => span.span_attributes?.name)).toEqual([ + "eve.session", "eve.turn", "eve.step", "researcher", + "eve.session", "eve.turn", "eve.step", "search", @@ -1319,13 +1405,17 @@ describe("braintrustEveHook", () => { const replaySpans = (await backgroundLogger.drain()) as Array< Record >; + expect( + replaySpans.find((span) => span.span_attributes?.name === "eve.session") + ?.span_id, + ).toBe(parentSession?.span_id); expect( replaySpans.find((span) => span.span_attributes?.name === "eve.turn") ?.span_id, ).toBe(parentTurn?.span_id); }); - it("parents root Eve turns under the active Braintrust span", async () => { + it("parents root Eve sessions under the active Braintrust span", async () => { const wildcard = braintrustEveHook().events?.["*"]; expect(wildcard).toBeDefined(); @@ -1357,11 +1447,19 @@ describe("braintrustEveHook", () => { const turn = spans.find( (span) => span.span_attributes?.name === "eve.turn", ); + const session = spans.find( + (span) => span.span_attributes?.name === "eve.session", + ); + expect(session?.span_id).toBe( + deterministicEveIdForTest("eve:session", "session-wrapped"), + ); + expect(session?.span_parents).toEqual([parent.spanId]); + expect(session?.root_span_id).toBe(parent.rootSpanId); expect(turn?.span_id).toBe( deterministicEveIdForTest("eve:turn", "session-wrapped", "turn-wrapped"), ); - expect(turn?.span_parents).toEqual([parent.spanId]); + expect(turn?.span_parents).toEqual([session?.span_id]); expect(turn?.root_span_id).toBe(parent.rootSpanId); }); @@ -1397,7 +1495,14 @@ describe("braintrustEveHook", () => { ), ).resolves.toBeUndefined(); - const spans = await backgroundLogger.drain(); - expect(spans).toEqual([]); + const spans = (await backgroundLogger.drain()) as Array< + Record + >; + expect(spans).toHaveLength(1); + expect(spans[0]).toMatchObject({ + metadata: {}, + span_attributes: { name: "eve.session", type: "task" }, + span_parents: [], + }); }); }); diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index 19b4d9cf7..299564f2c 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -1,6 +1,13 @@ import { toLoggedError } from "../core"; import { debugLogger } from "../../debug-logger"; -import { flush, logError, startSpan } from "../../logger"; +import { + NOOP_SPAN, + currentSpan, + flush, + logError, + startSpan, + withCurrent, +} from "../../logger"; import type { Span } from "../../logger"; import { SpanTypeAttribute, isObject } from "../../../util/index"; import type { @@ -21,6 +28,10 @@ type SpanState = { span: Span; }; +type SessionState = SpanState & { + sessionId: string; +}; + type StepState = SpanState & { input?: unknown; metrics: Record; @@ -127,10 +138,7 @@ function isEveHandleMessageStreamEvent( } class EveBridge { - private sessionsById = new Map< - string, - { metadata: Record } - >(); + private sessionsById = new Map(); private completedToolKeys = new Set(); private queuesBySessionId = new Map>(); private toolsByCallKey = new Map(); @@ -166,7 +174,7 @@ class EveBridge { ): Promise { switch (event.type) { case "session.started": - this.handleSessionStarted(event, ctx); + await this.handleSessionStarted(event, ctx, hookMetadata); return; case "turn.started": await this.handleTurnStarted(event, ctx, hookMetadata); @@ -218,17 +226,21 @@ class EveBridge { } } - private handleSessionStarted( + private async handleSessionStarted( event: Extract, ctx: unknown, - ): void { + hookMetadata?: Record, + ): Promise { const sessionId = sessionIdFromContext(ctx); - const metadata = modelMetadataFromRuntime(event.data.runtime); - if (!sessionId || Object.keys(metadata).length === 0) { + if (!sessionId) { return; } - this.sessionsById.set(sessionId, { metadata }); + const metadata = { + ...(hookMetadata ?? {}), + ...modelMetadataFromRuntime(event.data.runtime), + }; + await this.ensureSession(sessionId, ctx, metadata, eventTime(event)); for (const [key, turn] of this.turnsByKey) { if (!key.startsWith(`${sessionId}:`)) { continue; @@ -253,11 +265,14 @@ class EveBridge { return; } + const session = await this.ensureSession( + sessionId, + ctx, + hookMetadata ?? {}, + eventTime(event), + ); const key = turnKey(sessionId, event.data.turnId); - const metadata = { - ...(hookMetadata ?? {}), - ...(this.sessionsById.get(sessionId)?.metadata ?? {}), - }; + const metadata = { ...session.metadata }; const existing = this.turnsByKey.get(key); if (existing) { existing.metadata = { ...existing.metadata, ...metadata }; @@ -265,7 +280,7 @@ class EveBridge { return; } - const span = await this.startTurnSpan(sessionId, event, ctx, metadata); + const span = await this.startTurnSpan(session, event, metadata); span.log({ metadata }); this.turnsByKey.set(key, { key, @@ -832,6 +847,12 @@ class EveBridge { if (!sessionId) { return; } + const session = await this.ensureSession( + sessionId, + ctx, + {}, + eventTime(event), + ); for (const [key, turn] of this.turnsByKey) { if (!key.startsWith(`${sessionId}:`)) { @@ -862,6 +883,12 @@ class EveBridge { } } + logError( + session.span, + errorFromMessage(event.data.message, event.data.code, event.data.details), + ); + const endTime = eventTime(event); + session.span.end(endTime === undefined ? undefined : { endTime }); this.sessionsById.delete(sessionId); await this.flushInstrumentation(); @@ -875,6 +902,12 @@ class EveBridge { if (!sessionId) { return; } + const session = await this.ensureSession( + sessionId, + ctx, + {}, + eventTime(event), + ); for (const [key, turn] of this.turnsByKey) { if (!key.startsWith(`${sessionId}:`)) { @@ -899,11 +932,78 @@ class EveBridge { tool.endedByTurn = true; } } + session.span.log({ metadata: session.metadata }); + const endTime = eventTime(event); + session.span.end(endTime === undefined ? undefined : { endTime }); this.sessionsById.delete(sessionId); await this.flushInstrumentation(); } + private async ensureSession( + sessionId: string, + ctx: unknown, + metadata: Record, + startTime?: number, + ): Promise { + const existing = this.sessionsById.get(sessionId); + if (existing) { + existing.metadata = { ...existing.metadata, ...metadata }; + existing.span.log({ metadata: existing.metadata }); + return existing; + } + + const lineage = parentLineageFromContext(ctx); + if (lineage) { + await this.recordChildSessionOnParentSubagent(startTime, ctx, lineage); + } + const parentSubagent = lineage + ? this.toolsByCallKey.get(toolKey(lineage.sessionId, lineage.callId)) + : undefined; + const activeParent = currentSpan(); + const [eventId, spanId, parentSubagentSpanId, fallbackRootSpanId] = + await Promise.all([ + rowIdForSession(sessionId), + spanIdForSession(sessionId), + lineage + ? spanIdForSubagent(lineage.sessionId, lineage.callId) + : Promise.resolve(undefined), + lineage + ? rootSpanIdForLineage(lineage) + : rootSpanIdForSession(sessionId), + ]); + + const span = startSpanWithoutCurrentContext({ + event: { + id: eventId, + metadata, + }, + name: "eve.session", + parentSpanIds: + lineage && parentSubagentSpanId + ? { + rootSpanId: parentSubagent?.span.rootSpanId ?? fallbackRootSpanId, + spanId: parentSubagentSpanId, + } + : !Object.is(activeParent, NOOP_SPAN) + ? { + rootSpanId: activeParent.rootSpanId, + spanId: activeParent.spanId, + } + : { + parentSpanIds: [], + rootSpanId: fallbackRootSpanId, + }, + spanAttributes: { type: SpanTypeAttribute.TASK }, + spanId, + startTime, + }); + span.log({ metadata }); + const session = { metadata, sessionId, span }; + this.sessionsById.set(sessionId, session); + return session; + } + private async ensureTurn( event: Extract< EveHandleMessageStreamEvent, @@ -919,17 +1019,20 @@ class EveBridge { return undefined; } + const session = await this.ensureSession( + sessionId, + ctx, + hookMetadata ?? {}, + eventTime(event), + ); const key = turnKey(sessionId, event.data.turnId); const existing = this.turnsByKey.get(key); if (existing) { return existing; } - const metadata = { - ...(hookMetadata ?? {}), - ...(this.sessionsById.get(sessionId)?.metadata ?? {}), - }; - const span = await this.startTurnSpan(sessionId, event, ctx, metadata); + const metadata = { ...session.metadata }; + const span = await this.startTurnSpan(session, event, metadata); span.log({ metadata }); const state = { key, @@ -1178,46 +1281,29 @@ class EveBridge { } private async startTurnSpan( - sessionId: string, + session: SessionState, event: Extract< EveHandleMessageStreamEvent, { data: { readonly sequence: number; readonly turnId: string } } >, - ctx: unknown, metadata: Record, ): Promise { - const lineage = parentLineageFromContext(ctx); - if (lineage) { - await this.recordChildSessionOnParentSubagent(event, ctx, lineage); - } - const parentSubagent = lineage - ? this.toolsByCallKey.get(toolKey(lineage.sessionId, lineage.callId)) - : undefined; - const [eventId, spanId, parentSubagentSpanId, fallbackRootSpanId] = - await Promise.all([ - rowIdForTurn(sessionId, event.data.turnId), - spanIdForTurn(sessionId, event.data.turnId), - lineage - ? spanIdForSubagent(lineage.sessionId, lineage.callId) - : Promise.resolve(undefined), - lineage ? rootSpanIdForLineage(lineage) : Promise.resolve(undefined), - ]); - const rootSpanId = parentSubagent?.span.rootSpanId ?? fallbackRootSpanId; + const [eventId, spanId, sessionSpanId] = await Promise.all([ + rowIdForTurn(session.sessionId, event.data.turnId), + spanIdForTurn(session.sessionId, event.data.turnId), + spanIdForSession(session.sessionId), + ]); - return startSpan({ + return startSpanWithoutCurrentContext({ event: { id: eventId, metadata, }, name: "eve.turn", - ...(lineage && parentSubagentSpanId && rootSpanId - ? { - parentSpanIds: { - rootSpanId, - spanId: parentSubagentSpanId, - }, - } - : {}), + parentSpanIds: { + rootSpanId: session.span.rootSpanId, + spanId: sessionSpanId, + }, spanAttributes: { type: SpanTypeAttribute.TASK }, spanId, startTime: eventTime(event), @@ -1225,10 +1311,7 @@ class EveBridge { } private async recordChildSessionOnParentSubagent( - event: Extract< - EveHandleMessageStreamEvent, - { data: { readonly sequence: number; readonly turnId: string } } - >, + startTime: number | undefined, ctx: unknown, lineage: ParentLineage, ): Promise { @@ -1260,7 +1343,7 @@ class EveBridge { const parentTurn = this.turnsByKey.get( turnKey(lineage.sessionId, lineage.turnId), ); - const span = startSpan({ + const span = startSpanWithoutCurrentContext({ event: { id: eventId, metadata, @@ -1272,7 +1355,7 @@ class EveBridge { }, spanAttributes: { type: SpanTypeAttribute.TOOL }, spanId, - startTime: eventTime(event), + startTime, }); span.log({ metadata }); this.toolsByCallKey.set(toolKey(lineage.sessionId, lineage.callId), { @@ -1769,12 +1852,17 @@ async function rootSpanIdForSession(sessionId: string): Promise { } async function rootSpanIdForLineage(lineage: ParentLineage): Promise { - if (lineage.turnId) { - return spanIdForTurn(lineage.sessionId, lineage.turnId); - } return rootSpanIdForSession(lineage.rootSessionId); } +async function spanIdForSession(sessionId: string): Promise { + return deterministicEveId("eve:session", sessionId); +} + +async function rowIdForSession(sessionId: string): Promise { + return deterministicEveId("eve:row:session", sessionId); +} + async function spanIdForTurn( sessionId: string, turnId: string, @@ -1854,7 +1942,7 @@ function startChildSpan( parent: Span, args: Parameters[0], ): Span { - return startSpan({ + return startSpanWithoutCurrentContext({ ...args, parentSpanIds: { rootSpanId: parent.rootSpanId, @@ -1862,3 +1950,10 @@ function startChildSpan( }, }); } + +function startSpanWithoutCurrentContext( + args: Parameters[0], +): Span { + // Eve's deterministic parentSpanIds must win over any ambient Braintrust span. + return withCurrent(NOOP_SPAN, () => startSpan(args)); +} From 7513444a5da2d757b2531f850bc8e1c8d06057b9 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 10 Jul 2026 15:45:31 +0200 Subject: [PATCH 09/10] fix --- .../eve-instrumentation.span-tree.json | 6 - .../eve-instrumentation.span-tree.txt | 6 - .../agent/hooks/braintrust.ts | 2 + .../agent/instrumentation.ts | 2 + .../subagents/researcher/hooks/braintrust.ts | 2 + .../plugins/eve-plugin.test.ts | 185 ++++--- js/src/instrumentation/plugins/eve-plugin.ts | 486 ++++++++++-------- js/src/logger.ts | 32 +- 8 files changed, 379 insertions(+), 342 deletions(-) diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json index e45ffe2c7..8c02b064c 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -239,9 +239,6 @@ } } ], - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario" - }, "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "metadata": { "scenario": "eve-instrumentation", @@ -805,9 +802,6 @@ } } ], - "input": { - "message": "Run the Braintrust Eve instrumentation e2e scenario again" - }, "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "metadata": { "scenario": "eve-instrumentation", diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt index e135796ba..67fe5f49d 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -70,9 +70,6 @@ span_tree: │ │ "tokens": 6694 │ │ } │ ├── researcher [tool] - │ │ input: { - │ │ "message": "Run the Braintrust Eve instrumentation e2e scenario" - │ │ } │ │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" │ │ metadata: { │ │ "scenario": "eve-instrumentation", @@ -588,9 +585,6 @@ span_tree: │ "tokens": 6917 │ } ├── researcher [tool] - │ input: { - │ "message": "Run the Braintrust Eve instrumentation e2e scenario again" - │ } │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" │ metadata: { │ "scenario": "eve-instrumentation", diff --git a/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts b/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts index 2597dc318..fcac75fde 100644 --- a/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts +++ b/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts @@ -1,8 +1,10 @@ import { braintrustEveHook } from "braintrust"; +import { defineState } from "eve/context"; import { defineHook } from "eve/hooks"; export default defineHook( braintrustEveHook({ + defineState, metadata: { scenario: "eve-instrumentation", ...(process.env.BRAINTRUST_E2E_RUN_ID diff --git a/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts b/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts index 6ca571b23..fff8a737d 100644 --- a/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts +++ b/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts @@ -1,8 +1,10 @@ import { braintrustEveInstrumentation, initLogger } from "braintrust"; +import { defineState } from "eve/context"; import { defineInstrumentation } from "eve/instrumentation"; export default defineInstrumentation( braintrustEveInstrumentation({ + defineState, setup: ({ agentName }) => { initLogger({ projectName: process.env.BRAINTRUST_E2E_PROJECT_NAME || agentName, diff --git a/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts index 2597dc318..fcac75fde 100644 --- a/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts +++ b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts @@ -1,8 +1,10 @@ import { braintrustEveHook } from "braintrust"; +import { defineState } from "eve/context"; import { defineHook } from "eve/hooks"; export default defineHook( braintrustEveHook({ + defineState, metadata: { scenario: "eve-instrumentation", ...(process.env.BRAINTRUST_E2E_RUN_ID diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index f102c6f0f..dbaf0fe16 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -31,52 +31,21 @@ function deterministicEveIdForTest(...parts: string[]): string { .replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, "$1-$2-$3-$4-$5"); } -const EVE_CONTEXT_KEY_REGISTRY = Symbol.for("eve.context-key-registry"); -const EVE_CONTEXT_STORAGE = Symbol.for("eve.context-storage"); -const EVE_LLM_INPUT_STATE_KEY = "braintrust.eve.llmInputs"; - -function installFakeEveContext() { - const hadRegistry = Reflect.has(globalThis, EVE_CONTEXT_KEY_REGISTRY); - const hadStorage = Reflect.has(globalThis, EVE_CONTEXT_STORAGE); - const previousRegistry = Reflect.get(globalThis, EVE_CONTEXT_KEY_REGISTRY); - const previousStorage = Reflect.get(globalThis, EVE_CONTEXT_STORAGE); +function createFakeDefineState() { const values = new Map(); - const keyName = (key: unknown) => - key && typeof key === "object" && "name" in key - ? Reflect.get(key, "name") - : undefined; - const context = { - get(key: unknown) { - const name = keyName(key); - return typeof name === "string" ? values.get(name) : undefined; - }, - set(key: unknown, value: unknown) { - const name = keyName(key); - if (typeof name === "string") { - values.set(name, value); - } - }, - }; - - Reflect.set(globalThis, EVE_CONTEXT_KEY_REGISTRY, new Map()); - Reflect.set(globalThis, EVE_CONTEXT_STORAGE, { - getStore: () => context, - }); - return { - values, - restore() { - if (hadRegistry) { - Reflect.set(globalThis, EVE_CONTEXT_KEY_REGISTRY, previousRegistry); - } else { - Reflect.deleteProperty(globalThis, EVE_CONTEXT_KEY_REGISTRY); - } - if (hadStorage) { - Reflect.set(globalThis, EVE_CONTEXT_STORAGE, previousStorage); - } else { - Reflect.deleteProperty(globalThis, EVE_CONTEXT_STORAGE); - } + defineState(name: string, initial: () => T) { + return { + get: () => (values.has(name) ? (values.get(name) as T) : initial()), + update: (fn: (current: T) => T) => { + values.set( + name, + fn(values.has(name) ? (values.get(name) as T) : initial()), + ); + }, + }; }, + values, }; } @@ -90,14 +59,14 @@ describe("braintrustEveHook", () => { let backgroundLogger: ReturnType< typeof _exportsForTestingOnly.useTestBackgroundLogger >; - let restoreFakeEveContext: (() => void) | undefined; + let defineState: ReturnType["defineState"]; beforeAll(async () => { await _exportsForTestingOnly.simulateLoginForTests(); }); beforeEach(() => { - Reflect.deleteProperty(globalThis, Symbol.for("braintrust.eve.bridge")); + defineState = createFakeDefineState().defineState; backgroundLogger = _exportsForTestingOnly.useTestBackgroundLogger(); initLogger({ projectName: "eve-plugin.test.ts", @@ -107,14 +76,11 @@ describe("braintrustEveHook", () => { afterEach(() => { vi.restoreAllMocks(); - restoreFakeEveContext?.(); - restoreFakeEveContext = undefined; - Reflect.deleteProperty(globalThis, Symbol.for("braintrust.eve.bridge")); _exportsForTestingOnly.clearTestBackgroundLogger(); }); it("returns an Eve hook definition", () => { - const hook = braintrustEveHook(); + const hook = braintrustEveHook({ defineState }); expect(Object.keys(hook)).toEqual(["events"]); expect(typeof hook.events?.["*"]).toBe("function"); @@ -122,7 +88,10 @@ describe("braintrustEveHook", () => { it("returns an Eve instrumentation definition", () => { const setup = vi.fn(); - const instrumentation = braintrustEveInstrumentation({ setup }); + const instrumentation = braintrustEveInstrumentation({ + defineState, + setup, + }); expect(instrumentation).toMatchObject({ recordInputs: false, @@ -132,6 +101,11 @@ describe("braintrustEveHook", () => { expect(typeof instrumentation.events?.["step.started"]).toBe("function"); }); + it("requires Eve's defineState API", () => { + expect(() => braintrustEveHook(undefined as never)).toThrow(); + expect(() => braintrustEveInstrumentation(undefined as never)).toThrow(); + }); + it("exports Eve APIs from root and instrumentation entrypoints", () => { expect(braintrustExports.braintrustEveHook).toBe(braintrustEveHook); expect(braintrustExports.braintrustEveInstrumentation).toBe( @@ -144,10 +118,10 @@ describe("braintrustEveHook", () => { }); it("captures Eve instrumentation model input on the matching LLM step", async () => { - const fakeEve = installFakeEveContext(); - restoreFakeEveContext = fakeEve.restore; - const instrumentation = braintrustEveInstrumentation(); - const wildcard = braintrustEveHook().events?.["*"]; + const fakeEve = createFakeDefineState(); + defineState = fakeEve.defineState; + const instrumentation = braintrustEveInstrumentation({ defineState }); + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; expect(wildcard).toBeDefined(); const ctx: EveHookContext = { @@ -218,21 +192,21 @@ describe("braintrustEveHook", () => { role: "user", }, ]); - expect(fakeEve.values.get(EVE_LLM_INPUT_STATE_KEY)).toEqual({ - entries: [], + expect(fakeEve.values.get("braintrust.eve.tracing")).toMatchObject({ + llmInputs: [], }); }); it("skips missing or malformed Eve instrumentation state without throwing", async () => { - const instrumentation = braintrustEveInstrumentation(); + const instrumentation = braintrustEveInstrumentation({ defineState }); expect(() => instrumentation.events?.["step.started"]?.({ bad: true } as never), ).not.toThrow(); - const fakeEve = installFakeEveContext(); - restoreFakeEveContext = fakeEve.restore; - fakeEve.values.set(EVE_LLM_INPUT_STATE_KEY, { - entries: [{ input: { content: "not an array" }, key: "bad" }], + const fakeEve = createFakeDefineState(); + defineState = fakeEve.defineState; + fakeEve.values.set("braintrust.eve.tracing", { + llmInputs: [{ input: { content: "not an array" }, key: "bad" }], }); expect(() => instrumentation.events?.["step.started"]?.({ @@ -245,7 +219,7 @@ describe("braintrustEveHook", () => { }), ).not.toThrow(); - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; const ctx: EveHookContext = { session: { id: "session-no-state" } }; await wildcard?.( { @@ -292,6 +266,7 @@ describe("braintrustEveHook", () => { it("records a flat Eve turn with session model metadata", async () => { const wildcard = braintrustEveHook({ + defineState, metadata: { scenario: "eve-plugin-unit", testRunId: "test-run-flat-tree", @@ -549,7 +524,7 @@ describe("braintrustEveHook", () => { }); it("records each user message as a separate turn in one session", async () => { - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; expect(wildcard).toBeDefined(); const ctx: EveHookContext = { session: { id: "session-multi-turn" } }; @@ -595,7 +570,7 @@ describe("braintrustEveHook", () => { }); it("does not reconstruct later LLM inputs from earlier hook events", async () => { - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; expect(wildcard).toBeDefined(); const ctx: EveHookContext = { @@ -740,6 +715,7 @@ describe("braintrustEveHook", () => { it("merges late tool results into tool spans closed by turn completion", async () => { const wildcard = braintrustEveHook({ + defineState, metadata: { scenario: "eve-plugin-unit", testRunId: "test-run-late-tool-result", @@ -786,22 +762,32 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.030Z" }, type: "turn.completed", }); - await emit({ - data: { - result: { - callId: "call-late-search", - kind: "tool-result", - output: { title: "Late result" }, - toolName: "search", + const resumedWildcard = braintrustEveHook({ + defineState, + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-late-tool-result", + }, + }).events?.["*"]; + await resumedWildcard?.( + { + data: { + result: { + callId: "call-late-search", + kind: "tool-result", + output: { title: "Late result" }, + toolName: "search", + }, + sequence: 0, + status: "completed", + stepIndex: 0, + turnId: "turn-late-tool-result", }, - sequence: 0, - status: "completed", - stepIndex: 0, - turnId: "turn-late-tool-result", + meta: { at: "2026-01-01T00:00:00.040Z" }, + type: "action.result", }, - meta: { at: "2026-01-01T00:00:00.040Z" }, - type: "action.result", - }); + ctx, + ); const spans = (await backgroundLogger.drain()) as Array< Record @@ -812,6 +798,9 @@ describe("braintrustEveHook", () => { const tool = spans.find((span) => span.span_attributes?.name === "search"); expect(turns).toHaveLength(1); + expect( + spans.filter((span) => span.span_attributes?.name === "search"), + ).toHaveLength(1); expect(tool).toMatchObject({ input: { query: "late tool result" }, metadata: { @@ -830,6 +819,7 @@ describe("braintrustEveHook", () => { it("keeps deterministic tool state available after session completion", async () => { const wildcard = braintrustEveHook({ + defineState, metadata: { scenario: "eve-plugin-unit", testRunId: "test-run-late-after-session", @@ -905,6 +895,7 @@ describe("braintrustEveHook", () => { it("records result-only tool events without invented input", async () => { const wildcard = braintrustEveHook({ + defineState, metadata: { scenario: "eve-plugin-unit", testRunId: "test-run-result-only", @@ -951,7 +942,7 @@ describe("braintrustEveHook", () => { }); it("serializes events per session without blocking other sessions", async () => { - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; expect(wildcard).toBeDefined(); const sessionA: EveHookContext = { session: { id: "session-queue-a" } }; @@ -1002,7 +993,7 @@ describe("braintrustEveHook", () => { }); it("uses deterministic ids and nests local subagent sessions from Eve lineage", async () => { - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; expect(wildcard).toBeDefined(); const parentCtx: EveHookContext = { @@ -1100,7 +1091,7 @@ describe("braintrustEveHook", () => { }, ], sequence: 0, - stepIndex: 0, + stepIndex: 1, turnId: "turn-child", }, type: "actions.requested", @@ -1115,7 +1106,7 @@ describe("braintrustEveHook", () => { }, sequence: 0, status: "completed", - stepIndex: 0, + stepIndex: 1, turnId: "turn-child", }, type: "action.result", @@ -1171,7 +1162,7 @@ describe("braintrustEveHook", () => { type: "action.result", }); await emitParent({ - data: { sequence: 0, stepIndex: 0, turnId: "turn-parent" }, + data: { sequence: 0, stepIndex: 1, turnId: "turn-parent" }, type: "step.started", }); await emitParent({ @@ -1185,7 +1176,7 @@ describe("braintrustEveHook", () => { }, ], sequence: 0, - stepIndex: 0, + stepIndex: 1, turnId: "turn-parent", }, type: "actions.requested", @@ -1200,7 +1191,7 @@ describe("braintrustEveHook", () => { }, sequence: 0, status: "completed", - stepIndex: 0, + stepIndex: 1, turnId: "turn-parent", }, type: "action.result", @@ -1209,13 +1200,13 @@ describe("braintrustEveHook", () => { data: { finishReason: "tool-calls", sequence: 0, - stepIndex: 0, + stepIndex: 1, turnId: "turn-parent", }, type: "step.completed", }); await emitParent({ - data: { sequence: 0, stepIndex: 1, turnId: "turn-parent" }, + data: { sequence: 0, stepIndex: 2, turnId: "turn-parent" }, type: "step.started", }); await emitParent({ @@ -1223,7 +1214,7 @@ describe("braintrustEveHook", () => { finishReason: "stop", message: "Parent used the child result.", sequence: 0, - stepIndex: 1, + stepIndex: 2, turnId: "turn-parent", }, type: "message.completed", @@ -1232,7 +1223,7 @@ describe("braintrustEveHook", () => { data: { finishReason: "stop", sequence: 0, - stepIndex: 1, + stepIndex: 2, turnId: "turn-parent", }, type: "step.completed", @@ -1387,7 +1378,7 @@ describe("braintrustEveHook", () => { projectName: "eve-plugin.test.ts", projectId: "test-project-id", }); - const replay = braintrustEveHook().events?.["*"]; + const replay = braintrustEveHook({ defineState }).events?.["*"]; await replay?.( { data: { sequence: 0, turnId: "turn-parent" }, @@ -1406,17 +1397,15 @@ describe("braintrustEveHook", () => { Record >; expect( - replaySpans.find((span) => span.span_attributes?.name === "eve.session") - ?.span_id, - ).toBe(parentSession?.span_id); + replaySpans.find((span) => span.span_id === parentSession?.span_id), + ).toMatchObject({ _is_merge: true }); expect( - replaySpans.find((span) => span.span_attributes?.name === "eve.turn") - ?.span_id, - ).toBe(parentTurn?.span_id); + replaySpans.find((span) => span.span_id === parentTurn?.span_id), + ).toMatchObject({ _is_merge: true }); }); it("parents root Eve sessions under the active Braintrust span", async () => { - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; expect(wildcard).toBeDefined(); const ctx: EveHookContext = { @@ -1464,7 +1453,7 @@ describe("braintrustEveHook", () => { }); it("does not throw when Eve emits malformed events or failures", async () => { - const wildcard = braintrustEveHook().events?.["*"]; + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; expect(wildcard).toBeDefined(); await expect( diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index 299564f2c..63108989f 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -5,6 +5,7 @@ import { currentSpan, flush, logError, + resumeSpan, startSpan, withCurrent, } from "../../logger"; @@ -35,14 +36,12 @@ type SessionState = SpanState & { type StepState = SpanState & { input?: unknown; metrics: Record; - ordinal: number; output?: unknown; }; type TurnState = SpanState & { key: string; metrics: Record; - nextStepOrdinal: number; output?: unknown; stepsByIndex: Map; }; @@ -51,7 +50,6 @@ type ToolState = SpanState & { endedByTurn?: boolean; kind: "subagent" | "tool"; stepIndex?: number; - stepOrdinal?: number; turnKey: string; }; @@ -63,46 +61,60 @@ type ParentLineage = { turnSequence?: number; }; -const EVE_BRIDGE = Symbol.for("braintrust.eve.bridge"); -const EVE_CONTEXT_KEY_REGISTRY = Symbol.for("eve.context-key-registry"); -const EVE_CONTEXT_STORAGE = Symbol.for("eve.context-storage"); -const EVE_LLM_INPUT_STATE_KEY = "braintrust.eve.llmInputs"; -const EVE_LLM_INPUT_STATE = { name: EVE_LLM_INPUT_STATE_KEY }; -const MAX_STORED_LLM_INPUTS = 100; +type EveStateHandle = { + get(): T; + update(fn: (current: T) => T): void; +}; -type CapturedEveModelInput = readonly unknown[]; +type EveDefineState = (name: string, initial: () => T) => EveStateHandle; -type EveLlmInputState = { - entries: readonly { +type EveTraceState = { + metadata: Record; + startedSpanRows: readonly string[]; + stepStarts: readonly { + ordinal: number; + open: boolean; + stepIndex: number; + turnId: string; + }[]; + llmInputs: readonly { input: CapturedEveModelInput; key: string; }[]; }; +const EVE_TRACE_STATE_KEY = "braintrust.eve.tracing"; +const MAX_STORED_LLM_INPUTS = 100; + +type CapturedEveModelInput = readonly unknown[]; + /** Manual hook instrumentation for eve runtime stream events. */ -export function braintrustEveHook( - options: { metadata?: Record } = {}, -): EveHookDefinition { +export function braintrustEveHook(options: { + defineState: EveDefineState; + metadata?: Record; +}): EveHookDefinition { + const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState); + const bridge = new EveBridge(state); return { events: { "*": async (event: EveHandleMessageStreamEvent, ctx: EveHookContext) => { - await getEveBridge().handle(event, ctx, options.metadata); + await bridge.handle(event, ctx, options.metadata); }, }, }; } /** Eve instrumentation helper for logger setup and durable LLM input capture. */ -export function braintrustEveInstrumentation( - options: { - setup?: EveInstrumentationDefinition["setup"]; - } = {}, -): EveInstrumentationDefinition { +export function braintrustEveInstrumentation(options: { + defineState: EveDefineState; + setup?: EveInstrumentationDefinition["setup"]; +}): EveInstrumentationDefinition { + const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState); return { events: { "step.started": (input: EveInstrumentationStepStartedEventInput) => { try { - captureEveModelInput(input); + captureEveModelInput(state, input); } catch (error) { debugLogger.warn("Error in Eve LLM input capture:", error); } @@ -114,23 +126,6 @@ export function braintrustEveInstrumentation( }; } -function getEveBridge(): EveBridge { - const existing = Object.getOwnPropertyDescriptor( - globalThis, - EVE_BRIDGE, - )?.value; - if (existing instanceof EveBridge) { - return existing; - } - const bridge = new EveBridge(); - Object.defineProperty(globalThis, EVE_BRIDGE, { - configurable: true, - value: bridge, - writable: true, - }); - return bridge; -} - function isEveHandleMessageStreamEvent( event: unknown, ): event is EveHandleMessageStreamEvent { @@ -138,33 +133,123 @@ function isEveHandleMessageStreamEvent( } class EveBridge { + constructor(private readonly state: EveStateHandle) {} + private sessionsById = new Map(); private completedToolKeys = new Set(); - private queuesBySessionId = new Map>(); private toolsByCallKey = new Map(); private turnsByKey = new Map(); - handle( + private async startEveSpan( + args: Parameters[0], + ): Promise { + const rowId = args?.event?.id; + const started = + typeof rowId === "string" && + readEveTraceState(this.state).startedSpanRows.includes(rowId); + const span = withCurrent(NOOP_SPAN, () => + (started ? resumeSpan : startSpan)(args), + ); + if (!started && typeof rowId === "string") { + const flushed = await this.flushInstrumentation(); + if (!flushed) { + return span; + } + this.state.update((current) => { + const normalized = normalizeEveTraceState(current); + return normalized.startedSpanRows.includes(rowId) + ? normalized + : { + ...normalized, + startedSpanRows: [...normalized.startedSpanRows, rowId], + }; + }); + } + return span; + } + + private async startEveChildSpan( + parent: Span, + args: Parameters[0], + ): Promise { + return await this.startEveSpan({ + ...args, + parentSpanIds: { + rootSpanId: parent.rootSpanId, + spanId: parent.spanId, + }, + }); + } + + private stepOrdinal( + event: Extract, + ): number { + const state = readEveTraceState(this.state); + const stepsForIndex = state.stepStarts.filter( + (entry) => + entry.turnId === event.data.turnId && + entry.stepIndex === event.data.stepIndex, + ); + const previous = stepsForIndex.at(-1); + if (previous?.open) { + return previous.ordinal; + } + + const ordinal = state.stepStarts.filter( + (entry) => entry.turnId === event.data.turnId, + ).length; + this.state.update((current) => ({ + ...normalizeEveTraceState(current), + stepStarts: [ + ...state.stepStarts, + { + open: true, + ordinal, + stepIndex: event.data.stepIndex, + turnId: event.data.turnId, + }, + ], + })); + return ordinal; + } + + private markStepEnded(turnId: string, stepIndex: number): void { + this.state.update((current) => { + const state = normalizeEveTraceState(current); + let index = -1; + for (let i = state.stepStarts.length - 1; i >= 0; i--) { + const entry = state.stepStarts[i]; + if (entry?.turnId === turnId && entry.stepIndex === stepIndex) { + index = i; + break; + } + } + if (index < 0 || !state.stepStarts[index]?.open) { + return state; + } + return { + ...state, + stepStarts: state.stepStarts.map((entry, entryIndex) => + entryIndex === index ? { ...entry, open: false } : entry, + ), + }; + }); + } + + async handle( event: unknown, ctx: unknown, hookMetadata?: Record, ): Promise { if (!isEveHandleMessageStreamEvent(event)) { - return Promise.resolve(); - } - - const queueKey = queueKeyForEvent(event, ctx); - const previous = this.queuesBySessionId.get(queueKey) ?? Promise.resolve(); - const next = previous - .then(() => this.handleEvent(event, ctx, hookMetadata)) - .catch(() => undefined); - this.queuesBySessionId.set(queueKey, next); - void next.finally(() => { - if (this.queuesBySessionId.get(queueKey) === next) { - this.queuesBySessionId.delete(queueKey); - } - }); - return next; + return; + } + try { + await this.handleEvent(event, ctx, hookMetadata); + await this.flushInstrumentation(); + } catch (error) { + debugLogger.warn("Error in Eve hook instrumentation:", error); + } } private async handleEvent( @@ -240,6 +325,13 @@ class EveBridge { ...(hookMetadata ?? {}), ...modelMetadataFromRuntime(event.data.runtime), }; + this.state.update((current) => { + const normalized = normalizeEveTraceState(current); + return { + ...normalized, + metadata: { ...normalized.metadata, ...metadata }, + }; + }); await this.ensureSession(sessionId, ctx, metadata, eventTime(event)); for (const [key, turn] of this.turnsByKey) { if (!key.startsWith(`${sessionId}:`)) { @@ -286,7 +378,6 @@ class EveBridge { key, metadata, metrics: {}, - nextStepOrdinal: 0, span, stepsByIndex: new Map(), }); @@ -327,14 +418,16 @@ class EveBridge { }); const endTime = eventTime(event); existing.span.end(endTime === undefined ? undefined : { endTime }); + this.markStepEnded(event.data.turnId, event.data.stepIndex); } - const stepOrdinal = turn.nextStepOrdinal++; + const stepOrdinal = this.stepOrdinal(event); const metadata = { ...turn.metadata, ...(this.sessionsById.get(sessionId)?.metadata ?? {}), }; const input = consumeCapturedEveModelInput( + this.state, sessionId, event.data.turnId, event.data.stepIndex, @@ -343,7 +436,7 @@ class EveBridge { rowIdForStep(sessionId, event.data.turnId, stepOrdinal), spanIdForStep(sessionId, event.data.turnId, stepOrdinal), ]); - const span = startChildSpan(turn.span, { + const span = await this.startEveChildSpan(turn.span, { event: { id: eventId, ...(input !== undefined ? { input } : {}), @@ -360,7 +453,6 @@ class EveBridge { ...(input !== undefined ? { input } : {}), metadata, metrics: {}, - ordinal: stepOrdinal, span, }); } @@ -436,7 +528,6 @@ class EveBridge { return; } - const step = turn.stepsByIndex.get(event.data.stepIndex); const traceActions = event.data.actions.filter(isTraceableActionRequest); if (traceActions.length === 0) { return; @@ -444,12 +535,13 @@ class EveBridge { for (const action of traceActions) { if (isToolCallAction(action)) { - await this.startRequestedTool(event, turn, sessionId, action, step); + await this.startRequestedTool(event, turn, sessionId, action); } else if (isLocalSubagentCallAction(action)) { - await this.startRequestedSubagent(event, turn, sessionId, action, step); + await this.startRequestedSubagent(event, turn, sessionId, action); } } + const step = turn.stepsByIndex.get(event.data.stepIndex); if (!step) { return; } @@ -585,7 +677,7 @@ class EveBridge { } return; } - const span = startChildSpan(turn.span, { + const span = await this.startEveChildSpan(turn.span, { event: { id: eventId, metadata, @@ -772,6 +864,7 @@ class EveBridge { } turn.stepsByIndex.delete(event.data.stepIndex); } + this.markStepEnded(event.data.turnId, event.data.stepIndex); } private handleStepFailed( @@ -794,6 +887,7 @@ class EveBridge { const turn = this.turnForEvent(event, ctx); turn?.stepsByIndex.delete(event.data.stepIndex); + this.markStepEnded(event.data.turnId, event.data.stepIndex); } private async handleTurnCompleted( @@ -946,6 +1040,10 @@ class EveBridge { metadata: Record, startTime?: number, ): Promise { + metadata = { + ...readEveTraceState(this.state).metadata, + ...metadata, + }; const existing = this.sessionsById.get(sessionId); if (existing) { existing.metadata = { ...existing.metadata, ...metadata }; @@ -973,7 +1071,7 @@ class EveBridge { : rootSpanIdForSession(sessionId), ]); - const span = startSpanWithoutCurrentContext({ + const span = await this.startEveSpan({ event: { id: eventId, metadata, @@ -1038,7 +1136,6 @@ class EveBridge { key, metadata, metrics: {}, - nextStepOrdinal: 0, span, stepsByIndex: new Map(), }; @@ -1051,7 +1148,6 @@ class EveBridge { turn: TurnState, sessionId: string, action: EveRuntimeToolCallActionRequest, - step?: StepState, ): Promise { const key = toolKey(sessionId, action.callId); if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { @@ -1066,7 +1162,7 @@ class EveBridge { if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { return; } - const span = startChildSpan(turn.span, { + const span = await this.startEveChildSpan(turn.span, { event: { id: eventId, input: action.input, @@ -1083,7 +1179,6 @@ class EveBridge { metadata, span, stepIndex: event.data.stepIndex, - stepOrdinal: step?.ordinal, turnKey: turnKey(sessionId, event.data.turnId), }); } @@ -1093,7 +1188,6 @@ class EveBridge { turn: TurnState, sessionId: string, action: Extract, - step?: StepState, ): Promise { const key = toolKey(sessionId, action.callId); if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { @@ -1109,7 +1203,7 @@ class EveBridge { if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { return; } - const span = startChildSpan(turn.span, { + const span = await this.startEveChildSpan(turn.span, { event: { id: eventId, input: action.input, @@ -1126,7 +1220,6 @@ class EveBridge { metadata, span, stepIndex: event.data.stepIndex, - stepOrdinal: step?.ordinal, turnKey: turnKey(sessionId, event.data.turnId), }); } @@ -1152,7 +1245,7 @@ class EveBridge { if (existing) { return existing; } - const span = startChildSpan(turn.span, { + const span = await this.startEveChildSpan(turn.span, { event: { id: eventId, metadata, @@ -1196,7 +1289,7 @@ class EveBridge { if (existing) { return existing; } - const span = startChildSpan(turn.span, { + const span = await this.startEveChildSpan(turn.span, { event: { id: eventId, metadata, @@ -1238,7 +1331,7 @@ class EveBridge { if (existing) { return existing; } - const span = startChildSpan(turn.span, { + const span = await this.startEveChildSpan(turn.span, { event: { id: eventId, metadata, @@ -1294,7 +1387,7 @@ class EveBridge { spanIdForSession(session.sessionId), ]); - return startSpanWithoutCurrentContext({ + return await this.startEveSpan({ event: { id: eventId, metadata, @@ -1343,7 +1436,7 @@ class EveBridge { const parentTurn = this.turnsByKey.get( turnKey(lineage.sessionId, lineage.turnId), ); - const span = startSpanWithoutCurrentContext({ + const span = await this.startEveSpan({ event: { id: eventId, metadata, @@ -1431,11 +1524,13 @@ class EveBridge { this.turnsByKey.delete(key); } - private async flushInstrumentation(): Promise { + private async flushInstrumentation(): Promise { try { await flush(); + return true; } catch (error) { debugLogger.warn("Error in Eve flush instrumentation:", error); + return false; } } } @@ -1452,7 +1547,72 @@ function subagentNameFromContext(ctx: unknown): string | undefined { return typeof name === "string" ? name : undefined; } +function emptyEveTraceState(): EveTraceState { + return { + llmInputs: [], + metadata: {}, + startedSpanRows: [], + stepStarts: [], + }; +} + +function normalizeEveTraceState(state: unknown): EveTraceState { + if (!isObject(state)) { + return emptyEveTraceState(); + } + const metadata = isObject(state["metadata"]) ? state["metadata"] : {}; + const startedSpanRows = Array.isArray(state["startedSpanRows"]) + ? state["startedSpanRows"].filter( + (rowId): rowId is string => typeof rowId === "string", + ) + : []; + const llmInputs = Array.isArray(state["llmInputs"]) + ? state["llmInputs"].flatMap((entry): EveTraceState["llmInputs"] => { + if (!isObject(entry)) { + return []; + } + const key = entry["key"]; + const input = entry["input"]; + return typeof key === "string" && isCapturedModelInput(input) + ? [{ input, key }] + : []; + }) + : []; + const stepStarts = Array.isArray(state["stepStarts"]) + ? state["stepStarts"].flatMap((entry): EveTraceState["stepStarts"] => { + if (!isObject(entry)) { + return []; + } + const ordinal = entry["ordinal"]; + const open = entry["open"]; + const stepIndex = entry["stepIndex"]; + const turnId = entry["turnId"]; + return typeof ordinal === "number" && + Number.isInteger(ordinal) && + ordinal >= 0 && + typeof open === "boolean" && + typeof stepIndex === "number" && + Number.isInteger(stepIndex) && + typeof turnId === "string" + ? [{ open, ordinal, stepIndex, turnId }] + : []; + }) + : []; + return { llmInputs, metadata: { ...metadata }, startedSpanRows, stepStarts }; +} + +function readEveTraceState( + state: EveStateHandle, +): EveTraceState { + try { + return normalizeEveTraceState(state.get()); + } catch { + return emptyEveTraceState(); + } +} + function captureEveModelInput( + state: EveStateHandle, input: EveInstrumentationStepStartedEventInput, ): void { if (!isObject(input)) { @@ -1482,39 +1642,38 @@ function captureEveModelInput( return; } - const state = readEveLlmInputState(); - if (!state) { - return; - } - const key = llmInputKey(sessionId, turnId, stepIndex); - const entries = state.entries.filter((entry) => entry.key !== key); - entries.push({ input: captured, key }); - writeEveLlmInputState({ - entries: entries.slice(-MAX_STORED_LLM_INPUTS), + state.update((current) => { + const normalized = normalizeEveTraceState(current); + const llmInputs = [...normalized.llmInputs, { input: captured, key }]; + return { + ...normalized, + llmInputs: llmInputs.slice(-MAX_STORED_LLM_INPUTS), + }; }); } function consumeCapturedEveModelInput( + state: EveStateHandle, sessionId: string, turnId: string, stepIndex: number, ): CapturedEveModelInput | undefined { try { - const state = readEveLlmInputState(); - if (!state) { - return undefined; - } - const key = llmInputKey(sessionId, turnId, stepIndex); - const entry = state.entries.find((candidate) => candidate.key === key); + const current = readEveTraceState(state); + const entry = current.llmInputs.find((candidate) => candidate.key === key); if (!entry) { return undefined; } - writeEveLlmInputState({ - entries: state.entries.filter((candidate) => candidate.key !== key), - }); + const index = current.llmInputs.indexOf(entry); + state.update((value) => ({ + ...normalizeEveTraceState(value), + llmInputs: current.llmInputs.filter( + (_, candidateIndex) => candidateIndex !== index, + ), + })); return entry.input; } catch (error) { debugLogger.warn("Error in Eve LLM input consumption:", error); @@ -1552,103 +1711,6 @@ function capturedModelInput( } } -function readEveLlmInputState(): EveLlmInputState | undefined { - const context = activeEveContext(); - const key = eveLlmInputStateKey(); - if (!context || !key) { - return undefined; - } - - const get = context["get"]; - if (typeof get !== "function") { - return undefined; - } - - let state: unknown; - try { - state = get.call(context, key); - } catch { - return undefined; - } - if (!isObject(state)) { - return { entries: [] }; - } - - const entries = state["entries"]; - if (!Array.isArray(entries)) { - return { entries: [] }; - } - - return { - entries: entries.flatMap((entry): EveLlmInputState["entries"] => { - if (!isObject(entry)) { - return []; - } - const entryKey = entry["key"]; - const entryInput = entry["input"]; - if (typeof entryKey !== "string" || !isCapturedModelInput(entryInput)) { - return []; - } - return [{ input: entryInput, key: entryKey }]; - }), - }; -} - -function writeEveLlmInputState(state: EveLlmInputState): void { - const context = activeEveContext(); - const key = eveLlmInputStateKey(); - if (!context || !key) { - return; - } - - const set = context["set"]; - if (typeof set !== "function") { - return; - } - - try { - set.call(context, key, state); - } catch { - return; - } -} - -function activeEveContext(): Record | undefined { - const storage = Object.getOwnPropertyDescriptor( - globalThis, - EVE_CONTEXT_STORAGE, - )?.value; - if (!isObject(storage)) { - return undefined; - } - - const getStore = storage["getStore"]; - if (typeof getStore !== "function") { - return undefined; - } - - const context = getStore.call(storage); - return isObject(context) ? context : undefined; -} - -function eveLlmInputStateKey(): object | undefined { - const registry = Object.getOwnPropertyDescriptor( - globalThis, - EVE_CONTEXT_KEY_REGISTRY, - )?.value; - if (!(registry instanceof Map)) { - return undefined; - } - - const existing = registry.get(EVE_LLM_INPUT_STATE_KEY); - if (isObject(existing)) { - return existing; - } - - registry.set(EVE_LLM_INPUT_STATE_KEY, EVE_LLM_INPUT_STATE); - return EVE_LLM_INPUT_STATE; -} - function isCapturedModelInput(input: unknown): input is CapturedEveModelInput { return Array.isArray(input); } @@ -1700,16 +1762,6 @@ function sessionIdFromContext(ctx: unknown): string | undefined { return typeof id === "string" ? id : undefined; } -function queueKeyForEvent(event: Record, ctx: unknown) { - if (isObject(event.data)) { - const sessionId = event.data["sessionId"]; - if (typeof sessionId === "string") { - return sessionId; - } - } - return sessionIdFromContext(ctx) ?? "__unknown__"; -} - function toolMetadataFromTurn(turn: TurnState): Record { const { model: _model, provider: _provider, ...metadata } = turn.metadata; return metadata; @@ -1880,21 +1932,21 @@ async function rowIdForTurn( async function spanIdForStep( sessionId: string, turnId: string, - stepOrdinal: number, + stepIndex: number, ): Promise { - return deterministicEveId("eve:step", sessionId, turnId, String(stepOrdinal)); + return deterministicEveId("eve:step", sessionId, turnId, String(stepIndex)); } async function rowIdForStep( sessionId: string, turnId: string, - stepOrdinal: number, + stepIndex: number, ): Promise { return deterministicEveId( "eve:row:step", sessionId, turnId, - String(stepOrdinal), + String(stepIndex), ); } @@ -1937,23 +1989,3 @@ async function deterministicEveId(...parts: string[]): Promise { const hex = bytes.map((byte) => byte.toString(16).padStart(2, "0")).join(""); return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; } - -function startChildSpan( - parent: Span, - args: Parameters[0], -): Span { - return startSpanWithoutCurrentContext({ - ...args, - parentSpanIds: { - rootSpanId: parent.rootSpanId, - spanId: parent.spanId, - }, - }); -} - -function startSpanWithoutCurrentContext( - args: Parameters[0], -): Span { - // Eve's deterministic parentSpanIds must win over any ambient Braintrust span. - return withCurrent(NOOP_SPAN, () => startSpan(args)); -} diff --git a/js/src/logger.ts b/js/src/logger.ts index e9cfdcce6..1f0aaf15e 100644 --- a/js/src/logger.ts +++ b/js/src/logger.ts @@ -247,6 +247,12 @@ export type SetCurrentArg = { setCurrent?: boolean }; type StartSpanEventArgs = ExperimentLogPartialArgs & Partial; +const RESUME_SPAN = Symbol("braintrust.resume-span"); + +type ResumeSpanArgs = StartSpanArgs & { + readonly [RESUME_SPAN]?: true; +}; + export type StartSpanArgs = { name?: string; type?: SpanType; @@ -5570,6 +5576,20 @@ export function startSpan( return startSpanAndIsLogger(args).span; } +/** + * Reconstruct a span whose initial row has already been written by another + * process. This is intentionally kept off the root export surface for + * integrations that need durable, cross-process span lifecycles. + */ +export function resumeSpan( + args?: StartSpanArgs & AsyncFlushArg & OptionalStateArg, +): Span { + return startSpan({ + ...args, + [RESUME_SPAN]: true, + } as ResumeSpanArgs & AsyncFlushArg & OptionalStateArg); +} + /** * Flush any pending rows to the server. */ @@ -6828,7 +6848,8 @@ export class SpanImpl implements Span { parentSpanIds: ParentSpanIds | MultiParentSpanIds | undefined; defaultRootType?: SpanType; spanId?: string; - } & Omit, + } & Omit & + ResumeSpanArgs, ) { this._state = args.state; @@ -6895,10 +6916,11 @@ export class SpanImpl implements Span { this._rootSpanId = resolvedIds.rootSpanId; this._spanParents = resolvedIds.spanParents; - // The first log is a replacement, but subsequent logs to the same span - // object will be merges. - this.isMerge = false; - this.logInternal({ event, internalData }); + // The first log is normally a replacement. Resumed spans already have a + // durable row, so their first write must be a merge and must not replace + // structural fields such as the original start time. + this.isMerge = args[RESUME_SPAN] === true; + this.logInternal({ event, ...(this.isMerge ? {} : { internalData }) }); this.isMerge = true; } From 394ccced21048acf66e748554832e53741bce6d0 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 10 Jul 2026 16:49:43 +0200 Subject: [PATCH 10/10] durable tracking --- .agents/skills/instrumentation/SKILL.md | 1 + .../eve-instrumentation.span-tree.json | 6 + .../eve-instrumentation.span-tree.txt | 6 + .../plugins/eve-plugin.test.ts | 52 +++- js/src/instrumentation/plugins/eve-plugin.ts | 245 +++++++++--------- js/src/logger.ts | 32 +-- 6 files changed, 178 insertions(+), 164 deletions(-) diff --git a/.agents/skills/instrumentation/SKILL.md b/.agents/skills/instrumentation/SKILL.md index a47b9a744..d3ccd2b5d 100644 --- a/.agents/skills/instrumentation/SKILL.md +++ b/.agents/skills/instrumentation/SKILL.md @@ -36,6 +36,7 @@ Map the change before editing: - Promise/stream behavior must be preserved. Patches need to keep subclass/helper semantics intact. - Contain instrumentation failures. Extraction/logging bugs should be logged or ignored as appropriate, but must not break the user call path. - Use the SDK `debugLogger` for SDK instrumentation diagnostics. Do not call `console.*` directly from instrumentation code; direct console use should stay inside the debug logger implementation or another explicitly justified exception. +- Pass `Error` objects directly as `span.log({ error })` values. The SDK serializes errors correctly, so do not add local helpers that manually turn errors into message/stack strings unless an external API requires a non-`Error` representation. - Log only the useful surface. Prefer narrow, stable payloads over dumping full request/response objects; exclude redundant or overly large data when possible. - We want to limit our instrumentation to operations that are relevant for AI generations and operations (LLMs, embeddings, media generation, ...). Things like creating entities on platforms (CRUD for Workflows of Agent entities) is irrelevant to us. - When building instrumentation, we should always have a vendored type/interface for what we are wrapping. The type or interface should not be larger than what is relevant to the instrumentation. The type or interface should be used for typing tracing channels and also should be used to assert the type on whatever is passed into wrappers as soon as the wrapper has verified that the passed in value is plausibly what should be wrapped. diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json index 8c02b064c..e45ffe2c7 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -239,6 +239,9 @@ } } ], + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario" + }, "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "metadata": { "scenario": "eve-instrumentation", @@ -802,6 +805,9 @@ } } ], + "input": { + "message": "Run the Braintrust Eve instrumentation e2e scenario again" + }, "output": "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation", "metadata": { "scenario": "eve-instrumentation", diff --git a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt index 67fe5f49d..e135796ba 100644 --- a/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -70,6 +70,9 @@ span_tree: │ │ "tokens": 6694 │ │ } │ ├── researcher [tool] + │ │ input: { + │ │ "message": "Run the Braintrust Eve instrumentation e2e scenario" + │ │ } │ │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" │ │ metadata: { │ │ "scenario": "eve-instrumentation", @@ -585,6 +588,9 @@ span_tree: │ "tokens": 6917 │ } ├── researcher [tool] + │ input: { + │ "message": "Run the Braintrust Eve instrumentation e2e scenario again" + │ } │ output: "Researcher result: Eve instrumentation — https://eve.dev/docs/guides/instrumentation" │ metadata: { │ "scenario": "eve-instrumentation", diff --git a/js/src/instrumentation/plugins/eve-plugin.test.ts b/js/src/instrumentation/plugins/eve-plugin.test.ts index dbaf0fe16..cde5cb176 100644 --- a/js/src/instrumentation/plugins/eve-plugin.test.ts +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -714,8 +714,9 @@ describe("braintrustEveHook", () => { }); it("merges late tool results into tool spans closed by turn completion", async () => { + const eveState = createFakeDefineState(); const wildcard = braintrustEveHook({ - defineState, + defineState: eveState.defineState, metadata: { scenario: "eve-plugin-unit", testRunId: "test-run-late-tool-result", @@ -762,8 +763,31 @@ describe("braintrustEveHook", () => { meta: { at: "2026-01-01T00:00:00.030Z" }, type: "turn.completed", }); + expect(eveState.values.get("braintrust.eve.tracing")).toMatchObject({ + spanReferences: expect.arrayContaining([ + expect.objectContaining({ + exported: expect.any(String), + rootSpanId: deterministicEveIdForTest( + "eve:root", + "session-late-tool-result", + ), + rowId: deterministicEveIdForTest( + "eve:row:tool", + "session-late-tool-result", + "turn-late-tool-result", + "call-late-search", + ), + spanId: deterministicEveIdForTest( + "eve:tool", + "session-late-tool-result", + "turn-late-tool-result", + "call-late-search", + ), + }), + ]), + }); const resumedWildcard = braintrustEveHook({ - defineState, + defineState: eveState.defineState, metadata: { scenario: "eve-plugin-unit", testRunId: "test-run-late-tool-result", @@ -993,8 +1017,16 @@ describe("braintrustEveHook", () => { }); it("uses deterministic ids and nests local subagent sessions from Eve lineage", async () => { - const wildcard = braintrustEveHook({ defineState }).events?.["*"]; - expect(wildcard).toBeDefined(); + const parentEveState = createFakeDefineState(); + const childEveState = createFakeDefineState(); + const parentWildcard = braintrustEveHook({ + defineState: parentEveState.defineState, + }).events?.["*"]; + const childWildcard = braintrustEveHook({ + defineState: childEveState.defineState, + }).events?.["*"]; + expect(parentWildcard).toBeDefined(); + expect(childWildcard).toBeDefined(); const parentCtx: EveHookContext = { agent: { name: "eve-parent-agent" }, @@ -1015,9 +1047,9 @@ describe("braintrustEveHook", () => { }, }; const emitParent = (event: EveHandleMessageStreamEvent) => - wildcard?.(event, parentCtx); + parentWildcard?.(event, parentCtx); const emitChild = (event: EveHandleMessageStreamEvent) => - wildcard?.(event, childCtx); + childWildcard?.(event, childCtx); await emitParent({ data: { sequence: 0, turnId: "turn-parent" }, @@ -1302,6 +1334,9 @@ describe("braintrustEveHook", () => { expect(parentTurn).toBeDefined(); expect(subagentSpans).toHaveLength(1); expect(subagentSpans[0]?.span_id).toBe(subagentSpanId); + expect(subagentSpans[0]?.input).toEqual({ + message: "Find the relevant section", + }); expect(childTurn).toBeDefined(); expect(childSearch).toBeDefined(); expect(parentRead).toBeDefined(); @@ -1372,13 +1407,14 @@ describe("braintrustEveHook", () => { "eve.step", ]); - Reflect.deleteProperty(globalThis, Symbol.for("braintrust.eve.bridge")); backgroundLogger = _exportsForTestingOnly.useTestBackgroundLogger(); initLogger({ projectName: "eve-plugin.test.ts", projectId: "test-project-id", }); - const replay = braintrustEveHook({ defineState }).events?.["*"]; + const replay = braintrustEveHook({ + defineState: parentEveState.defineState, + }).events?.["*"]; await replay?.( { data: { sequence: 0, turnId: "turn-parent" }, diff --git a/js/src/instrumentation/plugins/eve-plugin.ts b/js/src/instrumentation/plugins/eve-plugin.ts index 63108989f..9124ce53c 100644 --- a/js/src/instrumentation/plugins/eve-plugin.ts +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -4,13 +4,13 @@ import { NOOP_SPAN, currentSpan, flush, - logError, - resumeSpan, startSpan, + updateSpan, withCurrent, } from "../../logger"; import type { Span } from "../../logger"; import { SpanTypeAttribute, isObject } from "../../../util/index"; +import { getCurrentUnixTimestamp } from "../../util"; import type { EveAssistantStepFinishReason, EveHandleMessageStreamEvent, @@ -26,7 +26,16 @@ import type { type SpanState = { metadata: Record; - span: Span; + span: EveSpan; +}; + +type EveSpan = Pick; + +type EveSpanReference = { + readonly exported: string; + readonly rootSpanId: string; + readonly rowId: string; + readonly spanId: string; }; type SessionState = SpanState & { @@ -57,8 +66,6 @@ type ParentLineage = { callId: string; rootSessionId: string; sessionId: string; - turnId?: string; - turnSequence?: number; }; type EveStateHandle = { @@ -70,7 +77,7 @@ type EveDefineState = (name: string, initial: () => T) => EveStateHandle; type EveTraceState = { metadata: Record; - startedSpanRows: readonly string[]; + spanReferences: readonly EveSpanReference[]; stepStarts: readonly { ordinal: number; open: boolean; @@ -132,6 +139,35 @@ function isEveHandleMessageStreamEvent( return isObject(event) && typeof event["type"] === "string"; } +class ResumedEveSpan implements EveSpan { + private endTime: number | undefined; + + constructor(private readonly reference: EveSpanReference) {} + + get rootSpanId(): string { + return this.reference.rootSpanId; + } + + get spanId(): string { + return this.reference.spanId; + } + + log(event: Parameters[0]): void { + updateSpan({ exported: this.reference.exported, ...event }); + } + + end(args?: Parameters[0]): number { + if (this.endTime === undefined) { + this.endTime = args?.endTime ?? getCurrentUnixTimestamp(); + updateSpan({ + exported: this.reference.exported, + metrics: { end: this.endTime }, + }); + } + return this.endTime; + } +} + class EveBridge { constructor(private readonly state: EveStateHandle) {} @@ -142,36 +178,51 @@ class EveBridge { private async startEveSpan( args: Parameters[0], - ): Promise { + ): Promise { const rowId = args?.event?.id; - const started = + const reference = typeof rowId === "string" && - readEveTraceState(this.state).startedSpanRows.includes(rowId); - const span = withCurrent(NOOP_SPAN, () => - (started ? resumeSpan : startSpan)(args), - ); - if (!started && typeof rowId === "string") { - const flushed = await this.flushInstrumentation(); - if (!flushed) { - return span; - } + readEveTraceState(this.state).spanReferences.find( + (candidate) => candidate.rowId === rowId, + ); + if (reference) { + return new ResumedEveSpan(reference); + } + + const span = withCurrent(NOOP_SPAN, () => startSpan(args)); + if (typeof rowId !== "string" || !(await this.flushInstrumentation())) { + return span; + } + + try { + const exported = await span.export(); + const reference = { + exported, + rootSpanId: span.rootSpanId, + rowId, + spanId: span.spanId, + }; this.state.update((current) => { const normalized = normalizeEveTraceState(current); - return normalized.startedSpanRows.includes(rowId) + return normalized.spanReferences.some( + (candidate) => candidate.rowId === rowId, + ) ? normalized : { ...normalized, - startedSpanRows: [...normalized.startedSpanRows, rowId], + spanReferences: [...normalized.spanReferences, reference], }; }); + } catch (error) { + debugLogger.warn("Error exporting Eve span for resumption:", error); } return span; } private async startEveChildSpan( - parent: Span, + parent: EveSpan, args: Parameters[0], - ): Promise { + ): Promise { return await this.startEveSpan({ ...args, parentSpanIds: { @@ -873,14 +924,13 @@ class EveBridge { ): void { const step = this.stepForEvent(event, ctx); if (step) { - logError( - step.span, - errorFromMessage( + step.span.log({ + error: errorFromMessage( event.data.message, event.data.code, event.data.details, ), - ); + }); const endTime = eventTime(event); step.span.end(endTime === undefined ? undefined : { endTime }); } @@ -922,10 +972,13 @@ class EveBridge { } this.endOpenChildrenForTurn(turn, eventTime(event)); - logError( - turn.span, - errorFromMessage(event.data.message, event.data.code, event.data.details), - ); + turn.span.log({ + error: errorFromMessage( + event.data.message, + event.data.code, + event.data.details, + ), + }); const endTime = eventTime(event); turn.span.end(endTime === undefined ? undefined : { endTime }); this.cleanupTurn(event, ctx); @@ -953,14 +1006,13 @@ class EveBridge { continue; } this.endOpenChildrenForTurn(turn, eventTime(event)); - logError( - turn.span, - errorFromMessage( + turn.span.log({ + error: errorFromMessage( event.data.message, event.data.code, event.data.details, ), - ); + }); const endTime = eventTime(event); turn.span.end(endTime === undefined ? undefined : { endTime }); this.turnsByKey.delete(key); @@ -977,10 +1029,13 @@ class EveBridge { } } - logError( - session.span, - errorFromMessage(event.data.message, event.data.code, event.data.details), - ); + session.span.log({ + error: errorFromMessage( + event.data.message, + event.data.code, + event.data.details, + ), + }); const endTime = eventTime(event); session.span.end(endTime === undefined ? undefined : { endTime }); this.sessionsById.delete(sessionId); @@ -1052,9 +1107,8 @@ class EveBridge { } const lineage = parentLineageFromContext(ctx); - if (lineage) { - await this.recordChildSessionOnParentSubagent(startTime, ctx, lineage); - } + // A child session has its own durable Eve context. It can link to the + // deterministic parent subagent span, but must not upsert that row itself. const parentSubagent = lineage ? this.toolsByCallKey.get(toolKey(lineage.sessionId, lineage.callId)) : undefined; @@ -1067,7 +1121,7 @@ class EveBridge { ? spanIdForSubagent(lineage.sessionId, lineage.callId) : Promise.resolve(undefined), lineage - ? rootSpanIdForLineage(lineage) + ? rootSpanIdForSession(lineage.rootSessionId) : rootSpanIdForSession(sessionId), ]); @@ -1380,7 +1434,7 @@ class EveBridge { { data: { readonly sequence: number; readonly turnId: string } } >, metadata: Record, - ): Promise { + ): Promise { const [eventId, spanId, sessionSpanId] = await Promise.all([ rowIdForTurn(session.sessionId, event.data.turnId), spanIdForTurn(session.sessionId, event.data.turnId), @@ -1403,62 +1457,6 @@ class EveBridge { }); } - private async recordChildSessionOnParentSubagent( - startTime: number | undefined, - ctx: unknown, - lineage: ParentLineage, - ): Promise { - const subagentName = subagentNameFromContext(ctx); - const existing = this.toolsByCallKey.get( - toolKey(lineage.sessionId, lineage.callId), - ); - if (existing) { - return; - } - - if (!lineage.turnId || typeof lineage.turnSequence !== "number") { - return; - } - - const metadata = {}; - const [eventId, spanId, parentTurnSpanId, rootSpanId] = await Promise.all([ - rowIdForSubagent(lineage.sessionId, lineage.callId), - spanIdForSubagent(lineage.sessionId, lineage.callId), - spanIdForTurn(lineage.sessionId, lineage.turnId), - rootSpanIdForLineage(lineage), - ]); - const pending = this.toolsByCallKey.get( - toolKey(lineage.sessionId, lineage.callId), - ); - if (pending) { - return; - } - const parentTurn = this.turnsByKey.get( - turnKey(lineage.sessionId, lineage.turnId), - ); - const span = await this.startEveSpan({ - event: { - id: eventId, - metadata, - }, - name: subagentName ?? "agent", - parentSpanIds: { - rootSpanId: parentTurn?.span.rootSpanId ?? rootSpanId, - spanId: parentTurnSpanId, - }, - spanAttributes: { type: SpanTypeAttribute.TOOL }, - spanId, - startTime, - }); - span.log({ metadata }); - this.toolsByCallKey.set(toolKey(lineage.sessionId, lineage.callId), { - kind: "subagent", - metadata, - span, - turnKey: turnKey(lineage.sessionId, lineage.turnId), - }); - } - private turnForEvent( event: Extract< EveHandleMessageStreamEvent, @@ -1535,23 +1533,11 @@ class EveBridge { } } -function subagentNameFromContext(ctx: unknown): string | undefined { - if (!isObject(ctx)) { - return undefined; - } - const agent = ctx["agent"]; - if (!isObject(agent)) { - return undefined; - } - const name = agent["name"]; - return typeof name === "string" ? name : undefined; -} - function emptyEveTraceState(): EveTraceState { return { llmInputs: [], metadata: {}, - startedSpanRows: [], + spanReferences: [], stepStarts: [], }; } @@ -1561,9 +1547,23 @@ function normalizeEveTraceState(state: unknown): EveTraceState { return emptyEveTraceState(); } const metadata = isObject(state["metadata"]) ? state["metadata"] : {}; - const startedSpanRows = Array.isArray(state["startedSpanRows"]) - ? state["startedSpanRows"].filter( - (rowId): rowId is string => typeof rowId === "string", + const spanReferences = Array.isArray(state["spanReferences"]) + ? state["spanReferences"].flatMap( + (entry): EveTraceState["spanReferences"] => { + if (!isObject(entry)) { + return []; + } + const exported = entry["exported"]; + const rootSpanId = entry["rootSpanId"]; + const rowId = entry["rowId"]; + const spanId = entry["spanId"]; + return typeof exported === "string" && + typeof rootSpanId === "string" && + typeof rowId === "string" && + typeof spanId === "string" + ? [{ exported, rootSpanId, rowId, spanId }] + : []; + }, ) : []; const llmInputs = Array.isArray(state["llmInputs"]) @@ -1598,7 +1598,7 @@ function normalizeEveTraceState(state: unknown): EveTraceState { : []; }) : []; - return { llmInputs, metadata: { ...metadata }, startedSpanRows, stepStarts }; + return { llmInputs, metadata: { ...metadata }, spanReferences, stepStarts }; } function readEveTraceState( @@ -1791,16 +1791,7 @@ function parentLineageFromContext(ctx: unknown): ParentLineage | undefined { return undefined; } - const turn = parent["turn"]; - const turnId = isObject(turn) ? turn["id"] : undefined; - const turnSequence = isObject(turn) ? turn["sequence"] : undefined; - return { - callId, - rootSessionId, - sessionId, - ...(typeof turnId === "string" ? { turnId } : {}), - ...(typeof turnSequence === "number" ? { turnSequence } : {}), - }; + return { callId, rootSessionId, sessionId }; } function isToolCallAction( @@ -1903,10 +1894,6 @@ async function rootSpanIdForSession(sessionId: string): Promise { return deterministicEveId("eve:root", sessionId); } -async function rootSpanIdForLineage(lineage: ParentLineage): Promise { - return rootSpanIdForSession(lineage.rootSessionId); -} - async function spanIdForSession(sessionId: string): Promise { return deterministicEveId("eve:session", sessionId); } diff --git a/js/src/logger.ts b/js/src/logger.ts index 384a688e1..6da93a9ae 100644 --- a/js/src/logger.ts +++ b/js/src/logger.ts @@ -262,12 +262,6 @@ export type SetCurrentArg = { setCurrent?: boolean }; type StartSpanEventArgs = ExperimentLogPartialArgs & Partial; -const RESUME_SPAN = Symbol("braintrust.resume-span"); - -type ResumeSpanArgs = { - readonly [RESUME_SPAN]?: true; -}; - export type StartSpanArgs = { name?: string; type?: SpanType; @@ -6194,20 +6188,6 @@ export function startSpan( return startSpanAndIsLogger(args).span; } -/** - * Reconstruct a span whose initial row has already been written by another - * process. This is intentionally kept off the root export surface for - * integrations that need durable, cross-process span lifecycles. - */ -export function resumeSpan( - args?: StartSpanArgs & AsyncFlushArg & OptionalStateArg, -): Span { - return startSpan({ - ...args, - [RESUME_SPAN]: true, - } as ResumeSpanArgs & AsyncFlushArg & OptionalStateArg); -} - /** * Flush any pending rows to the server. */ @@ -7497,8 +7477,7 @@ export class SpanImpl implements Span { defaultRootType?: SpanType; spanId?: string; propagatedState?: PropagatedState | undefined; - } & Omit & - ResumeSpanArgs, + } & Omit, ) { this._state = args.state; this._propagatedState = args.propagatedState; @@ -7566,11 +7545,10 @@ export class SpanImpl implements Span { this._rootSpanId = resolvedIds.rootSpanId; this._spanParents = resolvedIds.spanParents; - // The first log is normally a replacement. Resumed spans already have a - // durable row, so their first write must be a merge and must not replace - // structural fields such as the original start time. - this.isMerge = args[RESUME_SPAN] === true; - this.logInternal({ event, ...(this.isMerge ? {} : { internalData }) }); + // The first log is a replacement, but subsequent logs to the same span + // object will be merges. + this.isMerge = false; + this.logInternal({ event, internalData }); this.isMerge = true; }