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/.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/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..31b22e3ee --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/__cassettes__/eve-v0-20-0.cassette.json @@ -0,0 +1,4392 @@ +{ + "entries": [ + { + "callIndex": 0, + "id": "b18dba30bbb5cd34", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:16.190Z", + "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" + } + ], + "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_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" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f2066aa2d5b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:15 GMT", + "openai-processing-ms": "309", + "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": "req_4b470a9abe484a2ba4200567b7e4472f" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 1, + "id": "46596331f4831e14", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:18.151Z", + "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_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" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f2071cc775b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:17 GMT", + "openai-processing-ms": "241", + "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": "req_ebf26cc15d7449e29aac8ef627bcf657" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 2, + "id": "d1e7598a829dc70c", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:19.759Z", + "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_0b25bf43e95537d5006a50d355c0d081a183b20b8089fae34f", + "type": "item_reference" + }, + { + "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" + } + ], + "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_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" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f207c1eb75b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:19 GMT", + "openai-processing-ms": "327", + "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": "179992734", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "req_6384e299db814812a86667cade595a0e" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 3, + "id": "ced7f223095288e7", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:21.239Z", + "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" + } + ], + "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_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" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f208799825b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:20 GMT", + "openai-processing-ms": "298", + "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": "179992635", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "x-request-id": "req_3a2a06aa1cac4018a8a80392af1c04fc" + }, + "status": 200, + "statusText": "OK" + } + }, + { + "callIndex": 4, + "id": "004a21b65057e805", + "matchKey": "POST api.openai.com/v1/responses", + "recordedAt": "2026-07-10T11:11:22.603Z", + "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" + } + ], + "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_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" + }, + "headers": { + "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", + "alt-svc": "h3=\":443\"; ma=86400", + "cf-cache-status": "DYNAMIC", + "cf-ray": "a18f208f587b5b18-VIE", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "date": "Fri, 10 Jul 2026 11:11:22 GMT", + "openai-processing-ms": "299", + "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": "179992554", + "x-ratelimit-reset-requests": "2ms", + "x-ratelimit-reset-tokens": "2ms", + "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" + } + } + ], + "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..e45ffe2c7 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.json @@ -0,0 +1,1241 @@ +{ + "span_tree": [ + { + "name": "eve.session", + "type": "task", + "children": [ + { + "name": "eve.turn", + "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": [ + { + "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 + } + }, + { + "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", + "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 + } + }, + { + "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": [ + { + "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 + } + } + ], + "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 + } + } + ], + "metadata": { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + } + } + ], + "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" + }, + { + "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 + } + }, + { + "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" + }, + { + "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; 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": 6806, + "tokens": 6871 + } + } + ], + "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; 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": 120, + "prompt_cached_tokens": 13056, + "prompt_tokens": 20196, + "tokens": 20316 + } + }, + { + "name": "eve.turn", + "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" + }, + { + "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 + } + }, + { + "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": "" + } + } + ], + "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": "" + } + }, + { + "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" + } + ], + "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 + } + }, + { + "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" + }, + { + "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 + } + } + ], + "input": [ + { + "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", + "scenario": "eve-instrumentation", + "testRunId": "" + }, + "metrics": { + "completion_tokens": 121, + "prompt_cached_tokens": 19584, + "prompt_tokens": 20864, + "tokens": 20985 + } + } + ], + "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 new file mode 100644 index 000000000..e135796ba --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/__snapshots__/eve-instrumentation.span-tree.txt @@ -0,0 +1,1137 @@ +span_tree: +└── eve.session [task] + metadata: { + "model": "gpt-5.4-mini", + "provider": "openai", + "scenario": "eve-instrumentation", + "testRunId": "" + } + ├── 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; 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": 120, + │ "prompt_cached_tokens": 13056, + │ "prompt_tokens": 20196, + │ "tokens": 20316 + │ } + │ ├── 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": "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" + │ } + │ ] + │ 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": 6806, + │ "tokens": 6871 + │ } + └── eve.turn [task] + input: [ + { + "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", + "scenario": "eve-instrumentation", + "testRunId": "" + } + metrics: { + "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/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..fcac75fde --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/hooks/braintrust.ts @@ -0,0 +1,15 @@ +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 + ? { testRunId: process.env.BRAINTRUST_E2E_RUN_ID } + : {}), + }, + }), +); diff --git a/e2e/scenarios/eve-instrumentation/agent/instructions.md b/e2e/scenarios/eve-instrumentation/agent/instructions.md new file mode 100644 index 000000000..42aaa617f --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/instructions.md @@ -0,0 +1,11 @@ +You are a deterministic fixture agent for Braintrust Eve instrumentation tests. + +For every user task: + +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 researcher result, read 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..fff8a737d --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/instrumentation.ts @@ -0,0 +1,14 @@ +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/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..fcac75fde --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/agent/subagents/researcher/hooks/braintrust.ts @@ -0,0 +1,15 @@ +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 + ? { 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 new file mode 100644 index 000000000..679961e05 --- /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 nested 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..04cfe0d99 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/scenario.test.ts @@ -0,0 +1,239 @@ +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 { + findAllSpans, + findChildSpans, + findLatestChildSpan, +} 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 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", + childSession?.span.id, + )[0]; + const childSteps = findChildSpans(events, "eve.step", childTurn?.span.id); + const childSearch = findLatestChildSpan( + events, + "search", + 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([session?.span.id]); + expect(root?.metadata).toMatchObject({ + 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)); + 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(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", + scenario: "eve-instrumentation", + testRunId: expect.any(String), + }); + } + + 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"), + }); + expect(researcher?.metadata).toMatchObject({ + scenario: "eve-instrumentation", + testRunId: expect.any(String), + }); + 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([childSession?.span.id]); + expect(childTurn?.span.rootId).toEqual(root?.span.rootId); + expect(childTurn?.metadata).toMatchObject({ + scenario: "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(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", + scenario: "eve-instrumentation", + testRunId: expect.any(String), + }); + } + + 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"), + }); + expect(childSearch?.output).toMatchObject({ + title: "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", + }); + expect(read?.output).toMatchObject({ + section: "Runtime context", + 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 new file mode 100644 index 000000000..1be778de1 --- /dev/null +++ b/e2e/scenarios/eve-instrumentation/scenario.ts @@ -0,0 +1,265 @@ +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 { + continuationToken?: string; + sessionId?: string; + }; + if (!body.sessionId || !body.continuationToken) { + throw new Error( + `Eve session create did not return a sessionId and continuationToken`, + ); + } + + const seenSessionIds = new Set([body.sessionId]); + const nextIndex = await streamUntil( + baseUrl, + 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 { + 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 streamUntil( + baseUrl: string, + sessionId: string, + seenSessionIds: Set, + 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?startIndex=${startIndex}`, + { + 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 = ""; + let nextIndex = startIndex; + try { + while (true) { + const { done, value } = await reader.read(); + if (done) { + throw new Error(`Eve stream ended before ${until}`); + } + 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?: { childSessionId?: string; message?: string }; + type?: string; + }; + nextIndex++; + 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 === "subagent.called" && + typeof event.data?.childSessionId === "string" && + !seenSessionIds.has(event.data.childSessionId) + ) { + seenSessionIds.add(event.data.childSessionId); + childStreams.push( + streamUntil( + baseUrl, + event.data.childSessionId, + seenSessionIds, + "turn.completed", + ).then(() => undefined), + ); + } + if (event.type === until) { + await Promise.all(childStreams); + return nextIndex; + } + } + } + } 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 5e67fc1ba..14d1079e0 100644 --- a/js/src/exports.ts +++ b/js/src/exports.ts @@ -196,6 +196,10 @@ export { BraintrustMiddleware, wrapAISDKModel, } from "./wrappers/ai-sdk"; +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 385101d77..f398abbdb 100644 --- a/js/src/instrumentation/index.ts +++ b/js/src/instrumentation/index.ts @@ -23,6 +23,10 @@ export { braintrustFlueInstrumentation, braintrustFlueObserver, } from "./plugins/flue-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 new file mode 100644 index 000000000..cde5cb176 --- /dev/null +++ b/js/src/instrumentation/plugins/eve-plugin.test.ts @@ -0,0 +1,1533 @@ +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, + initLogger, + startSpan, + withCurrent, +} from "../../logger"; +import * as instrumentationExports from "../index"; +import { braintrustEveHook, braintrustEveInstrumentation } 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"); +} + +function createFakeDefineState() { + const values = new Map(); + return { + 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, + }; +} + +try { + configureNode(); +} catch { + // Best-effort initialization for test environments. +} + +describe("braintrustEveHook", () => { + let backgroundLogger: ReturnType< + typeof _exportsForTestingOnly.useTestBackgroundLogger + >; + let defineState: ReturnType["defineState"]; + + beforeAll(async () => { + await _exportsForTestingOnly.simulateLoginForTests(); + }); + + beforeEach(() => { + defineState = createFakeDefineState().defineState; + backgroundLogger = _exportsForTestingOnly.useTestBackgroundLogger(); + initLogger({ + projectName: "eve-plugin.test.ts", + projectId: "test-project-id", + }); + }); + + afterEach(() => { + vi.restoreAllMocks(); + _exportsForTestingOnly.clearTestBackgroundLogger(); + }); + + it("returns an Eve hook definition", () => { + const hook = braintrustEveHook({ defineState }); + + expect(Object.keys(hook)).toEqual(["events"]); + expect(typeof hook.events?.["*"]).toBe("function"); + }); + + it("returns an Eve instrumentation definition", () => { + const setup = vi.fn(); + const instrumentation = braintrustEveInstrumentation({ + defineState, + setup, + }); + + expect(instrumentation).toMatchObject({ + recordInputs: false, + recordOutputs: false, + setup, + }); + 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( + 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 = createFakeDefineState(); + defineState = fakeEve.defineState; + const instrumentation = braintrustEveInstrumentation({ defineState }); + const wildcard = braintrustEveHook({ defineState }).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([ + { + content: "Answer with the relevant Eve instrumentation detail.", + role: "system", + }, + { + content: "What should Braintrust capture?", + role: "user", + }, + ]); + expect(fakeEve.values.get("braintrust.eve.tracing")).toMatchObject({ + llmInputs: [], + }); + }); + + it("skips missing or malformed Eve instrumentation state without throwing", async () => { + const instrumentation = braintrustEveInstrumentation({ defineState }); + expect(() => + instrumentation.events?.["step.started"]?.({ bad: true } as never), + ).not.toThrow(); + + 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"]?.({ + modelInput: { + messages: [{ content: "hello", role: "user" }], + }, + session: { id: "session-malformed-state" }, + step: { index: 0 }, + turn: { id: "turn-malformed-state" }, + }), + ).not.toThrow(); + + const wildcard = braintrustEveHook({ defineState }).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({ + defineState, + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", + }, + }).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 = { + model: "braintrust-eve-mock", + provider: "eve-mock", + }; + + await 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", + }); + await emit({ + data: { sequence: 0, turnId: "turn-flat-tree" }, + meta: { at: "2026-01-01T00:00:00.010Z" }, + type: "turn.started", + }); + await emit({ + data: { + message: "Search then read", + sequence: 0, + turnId: "turn-flat-tree", + }, + meta: { at: "2026-01-01T00:00:00.020Z" }, + type: "message.received", + }); + await emit({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-flat-tree" }, + meta: { at: "2026-01-01T00:00:00.030Z" }, + type: "step.started", + }); + await 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", + }); + await 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", + }); + await 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", + }); + await emit({ + data: { sequence: 0, stepIndex: 1, turnId: "turn-flat-tree" }, + meta: { at: "2026-01-01T00:00:00.070Z" }, + type: "step.started", + }); + await 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", + }); + await 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", + }); + await 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 session = spans.find( + (span) => span.span_attributes?.name === "eve.session", + ); + const root = spans.find( + (span) => span.span_attributes?.name === "eve.turn", + ); + 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([ + "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: { + ...expectedModelMetadata, + scenario: "eve-plugin-unit", + testRunId: "test-run-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", + }, + span_parents: [session?.span_id], + }); + 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 step of steps) { + expect(step.metadata).toEqual({ + ...expectedModelMetadata, + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", + }); + } + expect(steps[0]?.input).toBeUndefined(); + expect(steps[1]?.input).toBeUndefined(); + expect(tool).toMatchObject({ + input: { query: "Eve instrumentation" }, + metadata: { + scenario: "eve-plugin-unit", + testRunId: "test-run-flat-tree", + }, + output: { hits: ["eve.dev/docs"] }, + span_attributes: { + name: "search", + type: "tool", + }, + span_parents: [root?.span_id], + }); + expect(tool?.metadata).not.toHaveProperty("model"); + expect(tool?.metadata).not.toHaveProperty("provider"); + 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", + }, + ], + }, + }, + ]); + expect(steps[1]?.output).toMatchObject([ + { + finish_reason: "stop", + message: { + content: "Here is the Eve instrumentation guide.", + role: "assistant", + }, + }, + ]); + }); + + it("records each user message as a separate turn in one session", async () => { + const wildcard = braintrustEveHook({ defineState }).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({ defineState }).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); + + await emit({ + data: { sequence: 0, turnId: "turn-incremental-tools" }, + type: "turn.started", + }); + await emit({ + data: { + message: "Search then read", + sequence: 0, + turnId: "turn-incremental-tools", + }, + type: "message.received", + }); + await emit({ + data: { sequence: 0, stepIndex: 0, turnId: "turn-incremental-tools" }, + type: "step.started", + }); + await 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", + }); + await 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", + }); + await 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", + }); + await 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", + }); + await emit({ + data: { + finishReason: "tool-calls", + sequence: 0, + stepIndex: 0, + turnId: "turn-incremental-tools", + }, + type: "step.completed", + }); + await emit({ + data: { sequence: 0, stepIndex: 1, turnId: "turn-incremental-tools" }, + type: "step.started", + }); + await emit({ + data: { + finishReason: "stop", + message: "Done.", + sequence: 0, + stepIndex: 1, + turnId: "turn-incremental-tools", + }, + type: "message.completed", + }); + await emit({ + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 1, + turnId: "turn-incremental-tools", + }, + type: "step.completed", + }); + await 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"), + ); + + expect(steps[0]?.output).toMatchObject([ + { + finish_reason: "tool_calls", + message: { + tool_calls: [{ id: "call-read", function: { name: "read" } }], + }, + }, + ]); + expect(steps[1]?.input).toBeUndefined(); + }); + + it("merges late tool results into tool spans closed by turn completion", async () => { + const eveState = createFakeDefineState(); + const wildcard = braintrustEveHook({ + defineState: eveState.defineState, + 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", + }); + 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: eveState.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", + }, + meta: { at: "2026-01-01T00:00:00.040Z" }, + type: "action.result", + }, + ctx, + ); + + 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( + spans.filter((span) => span.span_attributes?.name === "search"), + ).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("keeps deterministic tool state available after session completion", async () => { + const wildcard = braintrustEveHook({ + defineState, + 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({ + defineState, + 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({ defineState }).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 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" }, + 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) => + parentWildcard?.(event, parentCtx); + const emitChild = (event: EveHandleMessageStreamEvent) => + childWildcard?.(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: 1, + 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: 1, + 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: 1, 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: 1, + 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: 1, + turnId: "turn-parent", + }, + type: "action.result", + }); + await emitParent({ + data: { + finishReason: "tool-calls", + sequence: 0, + stepIndex: 1, + turnId: "turn-parent", + }, + type: "step.completed", + }); + await emitParent({ + data: { sequence: 0, stepIndex: 2, turnId: "turn-parent" }, + type: "step.started", + }); + await emitParent({ + data: { + finishReason: "stop", + message: "Parent used the child result.", + sequence: 0, + stepIndex: 2, + turnId: "turn-parent", + }, + type: "message.completed", + }); + await emitParent({ + data: { + finishReason: "stop", + sequence: 0, + stepIndex: 2, + 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 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", + "call-researcher", + ); + const parentTurn = spans.find( + (span) => + 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", + ); + const childTurn = spans.find( + (span) => + 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" && + 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(parentSession).toBeDefined(); + 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(); + expect(parentSteps).toHaveLength(3); + 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}$/, + ); + expect(parentTurn?.root_span_id).toMatch( + /^([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(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({}); + 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.session", + "eve.turn", + "eve.step", + "researcher", + "eve.session", + "eve.turn", + "eve.step", + "search", + "eve.step", + "read", + "eve.step", + ]); + + backgroundLogger = _exportsForTestingOnly.useTestBackgroundLogger(); + initLogger({ + projectName: "eve-plugin.test.ts", + projectId: "test-project-id", + }); + const replay = braintrustEveHook({ + defineState: parentEveState.defineState, + }).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_id === parentSession?.span_id), + ).toMatchObject({ _is_merge: true }); + expect( + 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({ defineState }).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", + ); + 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([session?.span_id]); + expect(turn?.root_span_id).toBe(parent.rootSpanId); + }); + + it("does not throw when Eve emits malformed events or failures", async () => { + const wildcard = braintrustEveHook({ defineState }).events?.["*"]; + expect(wildcard).toBeDefined(); + + await expect( + wildcard?.({ bad: true } as never, {}), + ).resolves.toBeUndefined(); + await expect( + wildcard?.( + { + data: { + code: "boom", + message: "step failed", + sequence: 0, + stepIndex: 0, + turnId: "turn-missing-session", + }, + type: "step.failed", + }, + {}, + ), + ).resolves.toBeUndefined(); + await expect( + wildcard?.( + { + data: { runtime: { modelId: 123 } }, + type: "session.started", + } as never, + { session: { id: "session-malformed-runtime" } }, + ), + ).resolves.toBeUndefined(); + + 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 new file mode 100644 index 000000000..9124ce53c --- /dev/null +++ b/js/src/instrumentation/plugins/eve-plugin.ts @@ -0,0 +1,1978 @@ +import { toLoggedError } from "../core"; +import { debugLogger } from "../../debug-logger"; +import { + NOOP_SPAN, + currentSpan, + flush, + startSpan, + updateSpan, + withCurrent, +} from "../../logger"; +import type { Span } from "../../logger"; +import { SpanTypeAttribute, isObject } from "../../../util/index"; +import { getCurrentUnixTimestamp } from "../../util"; +import type { + EveAssistantStepFinishReason, + EveHandleMessageStreamEvent, + EveHookContext, + EveHookDefinition, + EveInstrumentationDefinition, + EveInstrumentationStepStartedEventInput, + EveRuntimeActionRequest, + EveRuntimeActionResult, + EveRuntimeToolCallActionRequest, + EveRuntimeToolResultActionResult, +} from "../../vendor-sdk-types/eve"; + +type SpanState = { + metadata: Record; + span: EveSpan; +}; + +type EveSpan = Pick; + +type EveSpanReference = { + readonly exported: string; + readonly rootSpanId: string; + readonly rowId: string; + readonly spanId: string; +}; + +type SessionState = SpanState & { + sessionId: string; +}; + +type StepState = SpanState & { + input?: unknown; + metrics: Record; + output?: unknown; +}; + +type TurnState = SpanState & { + key: string; + metrics: Record; + output?: unknown; + stepsByIndex: Map; +}; + +type ToolState = SpanState & { + endedByTurn?: boolean; + kind: "subagent" | "tool"; + stepIndex?: number; + turnKey: string; +}; + +type ParentLineage = { + callId: string; + rootSessionId: string; + sessionId: string; +}; + +type EveStateHandle = { + get(): T; + update(fn: (current: T) => T): void; +}; + +type EveDefineState = (name: string, initial: () => T) => EveStateHandle; + +type EveTraceState = { + metadata: Record; + spanReferences: readonly EveSpanReference[]; + 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: { + 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 bridge.handle(event, ctx, options.metadata); + }, + }, + }; +} + +/** Eve instrumentation helper for logger setup and durable LLM input capture. */ +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(state, input); + } catch (error) { + debugLogger.warn("Error in Eve LLM input capture:", error); + } + }, + }, + recordInputs: false, + recordOutputs: false, + setup: options.setup, + }; +} + +function isEveHandleMessageStreamEvent( + event: unknown, +): event is EveHandleMessageStreamEvent { + 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) {} + + private sessionsById = new Map(); + private completedToolKeys = new Set(); + private toolsByCallKey = new Map(); + private turnsByKey = new Map(); + + private async startEveSpan( + args: Parameters[0], + ): Promise { + const rowId = args?.event?.id; + const reference = + typeof rowId === "string" && + 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.spanReferences.some( + (candidate) => candidate.rowId === rowId, + ) + ? normalized + : { + ...normalized, + spanReferences: [...normalized.spanReferences, reference], + }; + }); + } catch (error) { + debugLogger.warn("Error exporting Eve span for resumption:", error); + } + return span; + } + + private async startEveChildSpan( + parent: EveSpan, + 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; + } + try { + await this.handleEvent(event, ctx, hookMetadata); + await this.flushInstrumentation(); + } catch (error) { + debugLogger.warn("Error in Eve hook instrumentation:", error); + } + } + + private async handleEvent( + event: EveHandleMessageStreamEvent, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + switch (event.type) { + case "session.started": + await this.handleSessionStarted(event, ctx, hookMetadata); + return; + case "turn.started": + await this.handleTurnStarted(event, ctx, hookMetadata); + return; + case "message.received": + await this.handleMessageReceived(event, ctx, hookMetadata); + return; + case "step.started": + await this.handleStepStarted(event, ctx, hookMetadata); + return; + case "message.completed": + this.handleMessageCompleted(event, ctx); + return; + case "result.completed": + this.handleResultCompleted(event, ctx); + return; + case "actions.requested": + await this.handleActionsRequested(event, ctx, hookMetadata); + return; + case "action.result": + 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); + return; + case "step.failed": + this.handleStepFailed(event, ctx); + return; + case "turn.completed": + await this.handleTurnCompleted(event, ctx); + return; + case "turn.failed": + await this.handleTurnFailed(event, ctx); + return; + case "session.failed": + await this.handleSessionFailed(event, ctx); + return; + case "session.completed": + await this.handleSessionCompleted(event, ctx); + return; + default: + return; + } + } + + private async handleSessionStarted( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + + const metadata = { + ...(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}:`)) { + 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 }); + } + } + } + + private async handleTurnStarted( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + + const session = await this.ensureSession( + sessionId, + ctx, + hookMetadata ?? {}, + eventTime(event), + ); + const key = turnKey(sessionId, event.data.turnId); + const metadata = { ...session.metadata }; + const existing = this.turnsByKey.get(key); + if (existing) { + existing.metadata = { ...existing.metadata, ...metadata }; + existing.span.log({ metadata: existing.metadata }); + return; + } + + const span = await this.startTurnSpan(session, event, metadata); + span.log({ metadata }); + this.turnsByKey.set(key, { + key, + metadata, + metrics: {}, + span, + stepsByIndex: new Map(), + }); + } + + private async handleMessageReceived( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); + if (!turn) { + return; + } + + const input = [{ content: event.data.message, role: "user" }]; + turn.span.log({ input }); + } + + private async handleStepStarted( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): 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({ + ...(existing.input !== undefined ? { input: existing.input } : {}), + metadata: existing.metadata, + metrics: existing.metrics, + output: existing.output, + }); + const endTime = eventTime(event); + existing.span.end(endTime === undefined ? undefined : { endTime }); + this.markStepEnded(event.data.turnId, event.data.stepIndex); + } + + 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, + ); + const [eventId, spanId] = await Promise.all([ + rowIdForStep(sessionId, event.data.turnId, stepOrdinal), + spanIdForStep(sessionId, event.data.turnId, stepOrdinal), + ]); + const span = await this.startEveChildSpan(turn.span, { + event: { + id: eventId, + ...(input !== undefined ? { input } : {}), + metadata, + }, + name: "eve.step", + spanAttributes: { type: SpanTypeAttribute.LLM }, + spanId, + startTime: eventTime(event), + }); + span.log({ ...(input !== undefined ? { input } : {}), metadata }); + + turn.stepsByIndex.set(event.data.stepIndex, { + ...(input !== undefined ? { input } : {}), + metadata, + metrics: {}, + span, + }); + } + + private handleMessageCompleted( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (!step) { + 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), + index: 0, + message: { + content: event.data.message, + role: "assistant", + ...(Array.isArray(existingToolCalls) + ? { tool_calls: existingToolCalls } + : {}), + }, + }, + ]; + + const turn = this.turnForEvent(event, ctx); + if (turn && event.data.finishReason !== "tool-calls") { + turn.output = event.data.message; + } + } + + private handleResultCompleted( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (step) { + step.output = [ + { + finish_reason: "stop", + index: 0, + message: { + content: event.data.result, + role: "assistant", + }, + }, + ]; + } + + const turn = this.turnForEvent(event, ctx); + if (turn) { + turn.output = event.data.result; + } + } + + private async handleActionsRequested( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return; + } + + const traceActions = event.data.actions.filter(isTraceableActionRequest); + if (traceActions.length === 0) { + return; + } + + for (const action of traceActions) { + if (isToolCallAction(action)) { + await this.startRequestedTool(event, turn, sessionId, action); + } else if (isLocalSubagentCallAction(action)) { + await this.startRequestedSubagent(event, turn, sessionId, action); + } + } + + const step = turn.stepsByIndex.get(event.data.stepIndex); + if (!step) { + return; + } + + 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( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): 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, result.callId); + if (this.completedToolKeys.has(key)) { + return; + } + const tool = + this.toolsByCallKey.get(key) ?? + (await this.startSyntheticTool(event, ctx, result, hookMetadata)); + if (!tool) { + return; + } + const flushAfterCompletion = tool.endedByTurn === true; + + const failed = + event.data.status === "failed" || + result.isError === true || + event.data.error !== undefined; + tool.span.log({ + ...(failed + ? { + error: toLoggedError(event.data.error?.message ?? result.output), + } + : {}), + metadata: tool.metadata, + output: result.output, + }); + + const endTime = eventTime(event); + tool.span.end(endTime === undefined ? undefined : { endTime }); + this.toolsByCallKey.delete(key); + this.completedToolKeys.add(key); + if (flushAfterCompletion) { + await this.flushInstrumentation(); + } + } + + 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 = 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 = await this.startEveChildSpan(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, + 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; + } + const flushAfterCompletion = subagent.endedByTurn === true; + + this.completeSubagentSpan({ + endTime: eventTime(event), + error: event.data.error, + isError: event.data.status === "failed", + output: event.data.output, + spanState: subagent, + status: event.data.status, + }); + + this.toolsByCallKey.delete(key); + this.completedToolKeys.add(key); + if (flushAfterCompletion) { + await this.flushInstrumentation(); + } + } + + 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; + } + const flushAfterCompletion = subagent.endedByTurn === true; + + 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, + }); + + this.toolsByCallKey.delete(key); + this.completedToolKeys.add(key); + if (flushAfterCompletion) { + await this.flushInstrumentation(); + } + } + + private handleStepCompleted( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (!step) { + return; + } + + 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 } : {}), + }; + step.metrics = { ...step.metrics, ...metrics }; + if (Array.isArray(step.output) && isObject(step.output[0])) { + const finishReason = step.output[0].finish_reason; + if (typeof finishReason !== "string") { + 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, + }); + const endTime = eventTime(event); + step.span.end(endTime === undefined ? undefined : { endTime }); + + const turn = this.turnForEvent(event, ctx); + if (turn) { + for (const [key, value] of Object.entries(metrics)) { + turn.metrics[key] = (turn.metrics[key] ?? 0) + value; + } + turn.stepsByIndex.delete(event.data.stepIndex); + } + this.markStepEnded(event.data.turnId, event.data.stepIndex); + } + + private handleStepFailed( + event: Extract, + ctx: unknown, + ): void { + const step = this.stepForEvent(event, ctx); + if (step) { + 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 }); + } + + const turn = this.turnForEvent(event, ctx); + turn?.stepsByIndex.delete(event.data.stepIndex); + this.markStepEnded(event.data.turnId, event.data.stepIndex); + } + + private async handleTurnCompleted( + event: Extract, + ctx: unknown, + ): Promise { + const turn = this.turnForEvent(event, ctx); + if (!turn) { + return; + } + + 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.cleanupTurn(event, ctx); + + await this.flushInstrumentation(); + } + + private async handleTurnFailed( + event: Extract, + ctx: unknown, + ): Promise { + const turn = this.turnForEvent(event, ctx); + if (!turn) { + return; + } + + this.endOpenChildrenForTurn(turn, eventTime(event)); + 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); + + await this.flushInstrumentation(); + } + + private async handleSessionFailed( + event: Extract, + ctx: unknown, + ): Promise { + const sessionId = event.data.sessionId || sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + const session = await this.ensureSession( + sessionId, + ctx, + {}, + eventTime(event), + ); + + for (const [key, turn] of this.turnsByKey) { + if (!key.startsWith(`${sessionId}:`)) { + continue; + } + this.endOpenChildrenForTurn(turn, eventTime(event)); + 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); + } + + for (const [key, tool] of this.toolsByCallKey) { + if (key.startsWith(`${sessionId}:`)) { + const endTime = eventTime(event); + if (!tool.endedByTurn) { + tool.span.log({ metadata: tool.metadata }); + tool.span.end(endTime === undefined ? undefined : { endTime }); + tool.endedByTurn = true; + } + } + } + + 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); + + await this.flushInstrumentation(); + } + + private async handleSessionCompleted( + event: Extract, + ctx: unknown, + ): Promise { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + return; + } + const session = await this.ensureSession( + sessionId, + ctx, + {}, + eventTime(event), + ); + + for (const [key, turn] of this.turnsByKey) { + if (!key.startsWith(`${sessionId}:`)) { + continue; + } + 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; + } + } + 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 { + metadata = { + ...readEveTraceState(this.state).metadata, + ...metadata, + }; + 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); + // 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; + 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 + ? rootSpanIdForSession(lineage.rootSessionId) + : rootSpanIdForSession(sessionId), + ]); + + const span = await this.startEveSpan({ + 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, + { + data: { readonly sequence: number; readonly turnId: string }; + } + >, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + const sessionId = sessionIdFromContext(ctx); + if (!sessionId) { + 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 = { ...session.metadata }; + const span = await this.startTurnSpan(session, event, metadata); + span.log({ metadata }); + const state = { + key, + metadata, + metrics: {}, + span, + stepsByIndex: new Map(), + }; + this.turnsByKey.set(key, state); + return state; + } + + private async startRequestedTool( + event: Extract, + turn: TurnState, + sessionId: string, + action: EveRuntimeToolCallActionRequest, + ): Promise { + const key = toolKey(sessionId, action.callId); + if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { + return; + } + + 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 = await this.startEveChildSpan(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, + turnKey: turnKey(sessionId, event.data.turnId), + }); + } + + private async startRequestedSubagent( + event: Extract, + turn: TurnState, + sessionId: string, + action: Extract, + ): Promise { + const key = toolKey(sessionId, action.callId); + if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) { + return; + } + + const name = action.subagentName ?? action.name ?? "agent"; + 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 = await this.startEveChildSpan(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, + turnKey: turnKey(sessionId, event.data.turnId), + }); + } + + private async startSyntheticTool( + event: Extract, + ctx: unknown, + result: EveRuntimeToolResultActionResult, + hookMetadata?: Record, + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return undefined; + } + + 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 = await this.startEveChildSpan(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 async startSyntheticSubagent( + event: Extract, + ctx: unknown, + hookMetadata?: Record, + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return undefined; + } + + 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 = await this.startEveChildSpan(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 async startSyntheticSubagentResult( + event: Extract, + ctx: unknown, + result: Extract, + hookMetadata?: Record, + ): Promise { + const turn = await this.ensureTurn(event, ctx, hookMetadata); + const sessionId = sessionIdFromContext(ctx); + if (!turn || !sessionId) { + return undefined; + } + + 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 = await this.startEveChildSpan(turn.span, { + event: { + id: eventId, + metadata, + }, + name: result.subagentName, + spanAttributes: { type: SpanTypeAttribute.TOOL }, + spanId, + startTime: eventTime(event), + }); + span.log({ metadata }); + const state = { + kind: "subagent" 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 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( + session: SessionState, + event: Extract< + EveHandleMessageStreamEvent, + { data: { readonly sequence: number; readonly turnId: string } } + >, + metadata: Record, + ): Promise { + const [eventId, spanId, sessionSpanId] = await Promise.all([ + rowIdForTurn(session.sessionId, event.data.turnId), + spanIdForTurn(session.sessionId, event.data.turnId), + spanIdForSession(session.sessionId), + ]); + + return await this.startEveSpan({ + event: { + id: eventId, + metadata, + }, + name: "eve.turn", + parentSpanIds: { + rootSpanId: session.span.rootSpanId, + spanId: sessionSpanId, + }, + spanAttributes: { type: SpanTypeAttribute.TASK }, + spanId, + startTime: eventTime(event), + }); + } + + 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()) { + step.span.log({ + ...(step.input !== undefined ? { input: step.input } : {}), + metadata: step.metadata, + metrics: step.metrics, + output: step.output, + }); + step.span.end(endTime === undefined ? undefined : { endTime }); + } + turn.stepsByIndex.clear(); + + for (const tool of this.toolsByCallKey.values()) { + 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; + } + } + + 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 async flushInstrumentation(): Promise { + try { + await flush(); + return true; + } catch (error) { + debugLogger.warn("Error in Eve flush instrumentation:", error); + return false; + } + } +} + +function emptyEveTraceState(): EveTraceState { + return { + llmInputs: [], + metadata: {}, + spanReferences: [], + stepStarts: [], + }; +} + +function normalizeEveTraceState(state: unknown): EveTraceState { + if (!isObject(state)) { + return emptyEveTraceState(); + } + const metadata = isObject(state["metadata"]) ? state["metadata"] : {}; + 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"]) + ? 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 }, spanReferences, stepStarts }; +} + +function readEveTraceState( + state: EveStateHandle, +): EveTraceState { + try { + return normalizeEveTraceState(state.get()); + } catch { + return emptyEveTraceState(); + } +} + +function captureEveModelInput( + state: EveStateHandle, + 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 key = llmInputKey(sessionId, turnId, stepIndex); + 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 key = llmInputKey(sessionId, turnId, stepIndex); + const current = readEveTraceState(state); + const entry = current.llmInputs.find((candidate) => candidate.key === key); + if (!entry) { + return undefined; + } + + 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); + 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 + ? [{ content: instructions, role: "system" }] + : []), + ...messages, + ]; + try { + const cloned: unknown = JSON.parse(JSON.stringify(value)); + if (!Array.isArray(cloned)) { + return undefined; + } + return cloned; + } catch { + return undefined; + } +} + +function isCapturedModelInput(input: unknown): input is CapturedEveModelInput { + return Array.isArray(input); +} + +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 = 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 { + model: normalized.slice(slashIndex + 1), + provider: normalized.slice(0, slashIndex), + }; + } + + return { + model: normalized, + }; +} + +function sessionIdFromContext(ctx: unknown): string | undefined { + if (!isObject(ctx)) { + return undefined; + } + const session = ctx["session"]; + if (!isObject(session)) { + return undefined; + } + const id = session["id"]; + return typeof id === "string" ? id : undefined; +} + +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 = ctx.session; + if (!isObject(session)) { + return undefined; + } + const parent = session["parent"]; + if (!isObject(parent)) { + return undefined; + } + + const callId = parent["callId"]; + const rootSessionId = parent["rootSessionId"]; + const sessionId = parent["sessionId"]; + if ( + typeof callId !== "string" || + typeof rootSessionId !== "string" || + typeof sessionId !== "string" + ) { + return undefined; + } + + return { callId, rootSessionId, sessionId }; +} + +function isToolCallAction( + action: unknown, +): action is EveRuntimeToolCallActionRequest { + return ( + isObject(action) && + action["kind"] === "tool-call" && + typeof action["callId"] === "string" && + typeof action["toolName"] === "string" && + isObject(action["input"]) + ); +} + +function isLocalSubagentCallAction( + action: unknown, +): action is Extract { + return ( + isObject(action) && + action["kind"] === "subagent-call" && + typeof action["callId"] === "string" && + isObject(action["input"]) + ); +} + +function isTraceableActionRequest( + action: unknown, +): action is + | EveRuntimeToolCallActionRequest + | Extract { + return isToolCallAction(action) || isLocalSubagentCallAction(action); +} + +function isToolResult( + result: unknown, +): result is EveRuntimeToolResultActionResult { + return ( + isObject(result) && + result["kind"] === "tool-result" && + typeof result["callId"] === "string" && + typeof result["toolName"] === "string" + ); +} + +function isSubagentResult( + result: unknown, +): result is Extract { + return ( + isObject(result) && + result["kind"] === "subagent-result" && + typeof result["callId"] === "string" && + typeof result["subagentName"] === "string" + ); +} + +function normalizedFinishReason( + finishReason: EveAssistantStepFinishReason, +): string { + switch (finishReason) { + case "content-filter": + return "content_filter"; + case "tool-calls": + return "tool_calls"; + default: + return finishReason; + } +} + +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}`; +} + +async function rootSpanIdForSession(sessionId: string): Promise { + return deterministicEveId("eve:root", sessionId); +} + +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, +): 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, + stepIndex: number, +): Promise { + return deterministicEveId("eve:step", sessionId, turnId, String(stepIndex)); +} + +async function rowIdForStep( + sessionId: string, + turnId: string, + stepIndex: number, +): Promise { + return deterministicEveId( + "eve:row:step", + sessionId, + turnId, + String(stepIndex), + ); +} + +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); +} + +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)}`; +} diff --git a/js/src/vendor-sdk-types/eve.ts b/js/src/vendor-sdk-types/eve.ts new file mode 100644 index 000000000..025e80360 --- /dev/null +++ b/js/src/vendor-sdk-types/eve.ts @@ -0,0 +1,347 @@ +/** + * 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; + readonly parent?: { + readonly callId?: string; + readonly rootSessionId?: string; + readonly sessionId?: string; + readonly turn?: { + readonly id?: string; + readonly sequence?: number; + }; + }; + }; +} + +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"; + readonly name?: string; + } + | { + readonly callId: string; + readonly input: EveJsonObject; + readonly kind: "subagent-call"; + readonly name?: string; + readonly subagentName?: string; + }; + +export type EveRuntimeActionResult = + | EveRuntimeToolResultActionResult + | { + readonly callId: string; + readonly isError?: boolean; + 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; + }; + +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 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; + 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; + }; +} + +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; +}