Skip to content

Conversation

@dimavrem22
Copy link
Contributor

@dimavrem22 dimavrem22 commented Jan 29, 2026

  • [Alex] Async CDP monitors + unit tests

  • [Ray] DOM monitor

  • light refactor of network monitoring module:

    • moved constants out
    • track minor data for providing summary to agents
    • no fetch requests are blocked, but we skip logging telemetry, tracking, and analytics
  • Adapted the (soon to be deprecated) data store to use the data from async monitors:

    • required edits to guide agent and quick start scripts
  • NEW data store for network monitoring:

    • first attempt of a data store that removes the need for vectorstores
    • reads new data format from async monitors
    • implements convenience functions that allow agent to quickly query the network traffic data
    • unit tests added
  • Network SPY (beta): [THIS AGENT IS NOT FINALIZED - WILL BE FINALIZED WHEN WE REFACTOR DISCOVERY]

    • agent that has access to new network data store and tools to interact with it
    • autonomous mode allows mimicking first step of routine discovery (takes 5-10s on avg)
    • run_network_spy script allows chatting with network spy agent

    To run network spy:
    python scripts/run_network_spy.py --jsonl-path ./cdp_captures/network/events.jsonl

    To test how this will fit into discovery, use /autonomous <TASK>

@dimavrem22 dimavrem22 marked this pull request as ready for review January 30, 2026 20:44
@claude
Copy link

claude bot commented Jan 30, 2026

Code Review

I've completed a thorough review of PR #115 and found 6 issues that need to be addressed:


CLAUDE.md Violations

1. Lazy imports in bluebox/agents/network_spy.py

Lines 733-734:

def _tool_execute_python(self, tool_arguments: dict[str, Any]) -> dict[str, Any]:
"""Execute Python code with har_dict pre-loaded from NetworkDataStore."""
import io
import sys
code = tool_arguments.get("code", "")
if not code:
return {"error": "No code provided"}

import io
import sys

According to CLAUDE.md L49:

IMPORTANT: NO lazy imports! All imports must be at the top of the file

Fix: Move these imports to the top of the file.


2. Lazy import in bluebox/sdk/discovery.py

Line 87:

def _default_message_handler(self, message: RoutineDiscoveryMessage) -> None:
"""Default message handler that logs to console."""
from bluebox.data_models.routine_discovery.message import RoutineDiscoveryMessageType
if message.type == RoutineDiscoveryMessageType.INITIATED:
logger.info(f"🚀 {message.content}")
elif message.type == RoutineDiscoveryMessageType.PROGRESS_THINKING:

from bluebox.data_models.routine_discovery.message import RoutineDiscoveryMessageType

According to CLAUDE.md L49:

IMPORTANT: NO lazy imports! All imports must be at the top of the file

Fix: Move this import to the top of the file alongside the existing import from the same module.


Bugs

3. 🔴 CRITICAL: Arbitrary code execution via execute_python tool

File: bluebox/agents/network_spy.py, line 753

"har_dict": har_dict,
"json": json,
}
exec(code, exec_globals) # noqa: S102
output = captured_output.getvalue()
return {
"output": output if output else "(no output)",
}

The _tool_execute_python method uses exec() to execute arbitrary Python code without sandboxing:

exec_globals = {
    "har_dict": har_dict,
    "json": json,
}
exec(code, exec_globals)  # noqa: S102

Security Risk: The LLM analyzes network traffic that could contain malicious content. An attacker could embed prompt injection payloads in HTTP responses, headers, or URLs to trick the LLM into generating malicious code. The exec() has no restrictions on imports or builtins, allowing full system access.

Attack chain: Malicious network traffic → LLM context → prompt injection → LLM calls execute_python → arbitrary code execution

Recommendation: Either:

  1. Remove this tool entirely if not essential
  2. Implement proper sandboxing (restricted builtins, no imports, timeout)
  3. Use a safer alternative like AST-based expression evaluation for limited operations

4. unique_terms_found double-counts terms across search scopes

File: bluebox/agents/network_spy.py, lines 804-820

if "headers" in search_in:
headers_str = json.dumps(entry.request_headers).lower()
for term in terms_lower:
count = headers_str.count(term)
if count > 0:
unique_terms_found += 1
total_hits += count
if "headers" not in matched_in:
matched_in.append("headers")
# Search body
if "body" in search_in and entry.post_data:
post_data_str = json.dumps(entry.post_data) if isinstance(entry.post_data, (dict, list)) else str(entry.post_data)
body_lower = post_data_str.lower()
for term in terms_lower:
count = body_lower.count(term)
if count > 0:
unique_terms_found += 1
total_hits += count
if "body" not in matched_in:
matched_in.append("body")
if unique_terms_found > 0:
score = (total_hits / len(terms_lower)) * unique_terms_found
results.append({
"id": entry.request_id,
"method": entry.method,
"url": entry.url,
"matched_in": matched_in,
"unique_terms_found": unique_terms_found,
"total_hits": total_hits,
"score": round(score, 2),
})

The unique_terms_found counter is incremented for each term found in each scope (url, headers, body) independently:

if count > 0:
    unique_terms_found += 1  # Incremented per scope
    total_hits += count

Bug: If term "api" appears in both URL and headers, unique_terms_found becomes 2 instead of 1, inflating the relevance score.

Impact: With 3 terms each appearing in all 3 scopes, unique_terms_found would be 9 instead of 3, inflating scores by 3x.

Fix: Track found terms in a set and count unique terms only once:

found_terms = set()
# ... in each scope loop:
if count > 0:
    found_terms.add(term)
    total_hits += count
# ... after all scopes:
unique_terms_found = len(found_terms)

5. Hardcoded max iterations in autonomous mode

File: bluebox/agents/network_spy.py, line 1348

# Add finalize tool availability notice
if self._finalize_tool_registered:
# Get urgency based on iteration count
remaining_iterations = 10 - self._autonomous_iteration
if remaining_iterations <= 2:
finalize_notice = (
f"\n\n## CRITICAL: YOU MUST CALL finalize_result NOW!\n"
f"Only {remaining_iterations} iterations remaining. "
f"You MUST call `finalize_result` with your best findings immediately. "
f"Do NOT call any other tool - call finalize_result right now!"
)
elif remaining_iterations <= 4:
finalize_notice = (
f"\n\n## URGENT: Call finalize_result soon!\n"
f"Only {remaining_iterations} iterations remaining. "

remaining_iterations = 10 - self._autonomous_iteration

Bug: The run_autonomous method accepts a configurable max_iterations parameter (default 10), but _get_autonomous_system_prompt hardcodes 10 when computing remaining iterations.

Impact: If caller passes max_iterations=20, urgency notices trigger prematurely (at iteration 8 instead of 18), causing incorrect LLM behavior.

Fix: Pass max_iterations to _get_autonomous_system_prompt and use it:

remaining_iterations = max_iterations - self._autonomous_iteration

6. sessionId attached to all CDP commands including browser-level commands

File: bluebox/cdp/async_cdp_session.py, lines 259-260

"id": cmd_id,
"method": method,
"params": params or {},
}
if self.page_session_id:
msg["sessionId"] = self.page_session_id
msg_json = json.dumps(msg)
await self.ws.send(msg_json)
return cmd_id

if self.page_session_id:
    msg["sessionId"] = self.page_session_id

Bug: The code unconditionally attaches page_session_id to ALL CDP commands. However, browser-level commands (e.g., Target.getTargets, Target.attachToTarget) should NOT include a sessionId.

Impact: Sending sessionId with browser-level commands routes them to the wrong target, causing CDP protocol errors.

Fix: Only attach sessionId to page-level commands:

if self.page_session_id and is_page_level:
    msg["sessionId"] = self.page_session_id

(The is_page_level check already exists at line 241 for warning purposes.)


Summary

  • 2 CLAUDE.md violations (lazy imports)
  • 4 bugs including 1 critical security vulnerability

Please address these issues before merging.

@rayruizhiliao
Copy link
Contributor

Encountered a 400 error

[2026-01-30 16:46:09] INFO:bluebox.llms.llm_client:Instantiated LLMClient with model: gpt-5.1
You> do you see any DOM data?

╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ⚙ TOOL CALL ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Tool: search_har_responses_by_terms                                                                                                                                                                                                                                                                                      │
│                                                                                                                                                                                                                                                                                                                          │
│ Arguments:                                                                                                                                                                                                                                                                                                               │
│ {                                                                                                                                                                                                                                                                                                                        │
│   "terms": [                                                                                                                                                                                                                                                                                                             │
│     "DOM",                                                                                                                                                                                                                                                                                                               │
│     "dom",                                                                                                                                                                                                                                                                                                               │
│     "document",                                                                                                                                                                                                                                                                                                          │
│     "window",                                                                                                                                                                                                                                                                                                            │
│     "navigator",                                                                                                                                                                                                                                                                                                         │
│     "location",                                                                                                                                                                                                                                                                                                          │
│     "innerHTML",                                                                                                                                                                                                                                                                                                         │
│     "outerHTML",                                                                                                                                                                                                                                                                                                         │
│     "textContent",                                                                                                                                                                                                                                                                                                       │
│     "querySelector",                                                                                                                                                                                                                                                                                                     │
│     "querySelectorAll",                                                                                                                                                                                                                                                                                                  │
│     "getElementById",                                                                                                                                                                                                                                                                                                    │
│     "getElementsByClassName",                                                                                                                                                                                                                                                                                            │
│     "getElementsByTagName",                                                                                                                                                                                                                                                                                              │
│     "dataLayer",                                                                                                                                                                                                                                                                                                         │
│     "__NEXT_DATA__",                                                                                                                                                                                                                                                                                                     │
│     "__NUXT__",                                                                                                                                                                                                                                                                                                          │
│     "initialState",                                                                                                                                                                                                                                                                                                      │
│     "preloadedState",                                                                                                                                                                                                                                                                                                    │
│     "bootstrapData",                                                                                                                                                                                                                                                                                                     │
│     "hydration",                                                                                                                                                                                                                                                                                                         │
│     "SSR",                                                                                                                                                                                                                                                                                                               │
│     "CSR",                                                                                                                                                                                                                                                                                                               │
│     "state",                                                                                                                                                                                                                                                                                                             │
│     "props",                                                                                                                                                                                                                                                                                                             │
│     "root",                                                                                                                                                                                                                                                                                                              │
│     "appConfig",                                                                                                                                                                                                                                                                                                         │
│     "pageConfig",                                                                                                                                                                                                                                                                                                        │
│     "redux",                                                                                                                                                                                                                                                                                                             │
│     "store"                                                                                                                                                                                                                                                                                                              │
│   ]                                                                                                                                                                                                                                                                                                                      │
│ }                                                                                                                                                                                                                                                                                                                        │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✓ Tool executed
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Result ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ "terms_searched: 30\nresults_found: 20\nresults[20]{id,url,unique_terms_found,total_hits,score}:\n  interception-job-901.0,\"https://www.amtrak.com/services/data.stations.json\",2,1072,71.46666666666667\n  interception-job-937.0,\"https://www.amtrak.com/services/data.stations.json\",2,1072,71.46666666666667\n   │
│ interception-job-1473.0,\"https://assets.adobetarget.com/amtrakdigital/production/v1/rules.json\",5,282,47\n  interception-job-1081.0,\"https://media-us1.digital.nuance.com/media/launch/all_10006734.json?codeVersion=1768457234043\",7,95,22.166666666666664\n                                                        │
│ interception-job-1531.0,\"https://media-us1.digital.nuance.com/media/launch/all_10006734.json?codeVersion=1768457234043\",7,95,22.166666666666664\n  interception-job-894.0,\"https://collection.decibelinsight.net/i/14240/3228221/c.json\",8,18,4.8\n                                                                  │
│ interception-job-1386.0,\"https://collection.decibelinsight.net/i/14240/3228221/c.json\",8,18,4.8\n  interception-job-896.0,\"https://www.amtrak.com/services/staticmappingjson\",3,12,1.2000000000000002\n  interception-job-802.2,\"https://www.amtrak.com/home\",4,7,0.9333333333333333\n                             │
│ interception-job-1440.0,\"https://www.amtrak.com/content/amtrak/en-us/reference-data/dc-api/jcr:content/root/contentfragment.datasearch.json\",2,14,0.9333333333333333\n  interception-job-1277.0,\"https://www.amtrak.com/services/MapDataService/AutoCompleterArcgis/getResponseList?searchTerm=boston\",2,9,0.6\n     │
│ interception-job-900.0,\"https://www.amtrak.com/services/globalerrorjson\",3,5,0.5\n  interception-job-1392.0,\"https://www.amtrak.com/services/globalerrorjson\",3,5,0.5\n  interception-job-1326.0,\"https://www.amtrak.com/tickets/departure.html\",3,3,0.30000000000000004\n                                         │
│ interception-job-935.0,\"https://www.amtrak.com/content/amtrak/en-us/home.alertsService.emergency-alerts.json\",2,2,0.13333333333333333\n  interception-job-996.0,\"https://ct.pinterest.com/user/?tid=2612534113654&cb=1769806170224&dep=2%2CPAGE_LOAD\",2,2,0.13333333333333333\n                                      │
│ interception-job-1465.0,\"https://ct.pinterest.com/user/?tid=2612534113654&pd=%7B%22pin_unauth%22%3A%22dWlkPU9UTm1ZemxpT1RBdE5XTmpNaTAwWkRJeExXRmxNakV0WldaallUTmhOMk00WmpKag%22%7D&cb=1769806190608&dep=2%2CPAGE_LOAD\",2,2,0.13333333333333333\n                                                                       │
│ interception-job-939.0,\"https://www.amtrak.com/content/amtrak/en-us/api/terms-and-conditions/version.model.json\",1,2,0.06666666666666667\n  interception-job-936.0,\"https://www.amtrak.com/content/amtrak/en-us/api/terms-and-conditions/version.model.json\",1,2,0.06666666666666667\n                               │
│ interception-job-1320.0,\"https://www.amtrak.com/dotcom/journey-solution-option\",1,1,0.03333333333333333"                                                                                                                                                                                                               │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

[2026-01-30 16:47:23] ERROR:bluebox.agents.network_spy:Error in agent loop: Error code: 400 - {'error': {'message': "Invalid type for 'messages[2].tool_calls[0].id': expected a string, but got null instead.", 'type': 'invalid_request_error', 'param': 'messages[2].tool_calls[0].id', 'code': 'invalid_type'}}
Traceback (most recent call last):
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/agents/network_spy.py", line 1057, in _run_agent_loop
    response = self._process_streaming_response(messages)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/agents/network_spy.py", line 1123, in _process_streaming_response
    for item in self.llm_client.call_stream_sync(
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/llms/llm_client.py", line 243, in call_stream_sync
    yield from self._client.call_stream_sync(
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/llms/openai_client.py", line 706, in call_stream_sync
    stream = self._client.chat.completions.create(**kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/_utils/_utils.py", line 286, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py", line 1192, in create
    return self._post(
           ^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1259, in post
    return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1047, in request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid type for 'messages[2].tool_calls[0].id': expected a string, but got null instead.", 'type': 'invalid_request_error', 'param': 'messages[2].tool_calls[0].id', 'code': 'invalid_type'}}

⚠ Error: Error code: 400 - {'error': {'message': "Invalid type for 'messages[2].tool_calls[0].id': expected a string, but got null instead.", 'type': 'invalid_request_error', 'param': 'messages[2].tool_calls[0].id', 'code': 'invalid_type'}}
[2026-01-30 16:46:09] INFO:bluebox.llms.llm_client:Instantiated LLMClient with model: gpt-5.1
You> do you see any DOM data?

╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ⚙ TOOL CALL ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Tool: search_har_responses_by_terms                                                                                                                                                                                                                                                                                      │
│                                                                                                                                                                                                                                                                                                                          │
│ Arguments:                                                                                                                                                                                                                                                                                                               │
│ {                                                                                                                                                                                                                                                                                                                        │
│   "terms": [                                                                                                                                                                                                                                                                                                             │
│     "DOM",                                                                                                                                                                                                                                                                                                               │
│     "dom",                                                                                                                                                                                                                                                                                                               │
│     "document",                                                                                                                                                                                                                                                                                                          │
│     "window",                                                                                                                                                                                                                                                                                                            │
│     "navigator",                                                                                                                                                                                                                                                                                                         │
│     "location",                                                                                                                                                                                                                                                                                                          │
│     "innerHTML",                                                                                                                                                                                                                                                                                                         │
│     "outerHTML",                                                                                                                                                                                                                                                                                                         │
│     "textContent",                                                                                                                                                                                                                                                                                                       │
│     "querySelector",                                                                                                                                                                                                                                                                                                     │
│     "querySelectorAll",                                                                                                                                                                                                                                                                                                  │
│     "getElementById",                                                                                                                                                                                                                                                                                                    │
│     "getElementsByClassName",                                                                                                                                                                                                                                                                                            │
│     "getElementsByTagName",                                                                                                                                                                                                                                                                                              │
│     "dataLayer",                                                                                                                                                                                                                                                                                                         │
│     "__NEXT_DATA__",                                                                                                                                                                                                                                                                                                     │
│     "__NUXT__",                                                                                                                                                                                                                                                                                                          │
│     "initialState",                                                                                                                                                                                                                                                                                                      │
│     "preloadedState",                                                                                                                                                                                                                                                                                                    │
│     "bootstrapData",                                                                                                                                                                                                                                                                                                     │
│     "hydration",                                                                                                                                                                                                                                                                                                         │
│     "SSR",                                                                                                                                                                                                                                                                                                               │
│     "CSR",                                                                                                                                                                                                                                                                                                               │
│     "state",                                                                                                                                                                                                                                                                                                             │
│     "props",                                                                                                                                                                                                                                                                                                             │
│     "root",                                                                                                                                                                                                                                                                                                              │
│     "appConfig",                                                                                                                                                                                                                                                                                                         │
│     "pageConfig",                                                                                                                                                                                                                                                                                                        │
│     "redux",                                                                                                                                                                                                                                                                                                             │
│     "store"                                                                                                                                                                                                                                                                                                              │
│   ]                                                                                                                                                                                                                                                                                                                      │
│ }                                                                                                                                                                                                                                                                                                                        │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✓ Tool executed
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Result ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ "terms_searched: 30\nresults_found: 20\nresults[20]{id,url,unique_terms_found,total_hits,score}:\n  interception-job-901.0,\"https://www.amtrak.com/services/data.stations.json\",2,1072,71.46666666666667\n  interception-job-937.0,\"https://www.amtrak.com/services/data.stations.json\",2,1072,71.46666666666667\n   │
│ interception-job-1473.0,\"https://assets.adobetarget.com/amtrakdigital/production/v1/rules.json\",5,282,47\n  interception-job-1081.0,\"https://media-us1.digital.nuance.com/media/launch/all_10006734.json?codeVersion=1768457234043\",7,95,22.166666666666664\n                                                        │
│ interception-job-1531.0,\"https://media-us1.digital.nuance.com/media/launch/all_10006734.json?codeVersion=1768457234043\",7,95,22.166666666666664\n  interception-job-894.0,\"https://collection.decibelinsight.net/i/14240/3228221/c.json\",8,18,4.8\n                                                                  │
│ interception-job-1386.0,\"https://collection.decibelinsight.net/i/14240/3228221/c.json\",8,18,4.8\n  interception-job-896.0,\"https://www.amtrak.com/services/staticmappingjson\",3,12,1.2000000000000002\n  interception-job-802.2,\"https://www.amtrak.com/home\",4,7,0.9333333333333333\n                             │
│ interception-job-1440.0,\"https://www.amtrak.com/content/amtrak/en-us/reference-data/dc-api/jcr:content/root/contentfragment.datasearch.json\",2,14,0.9333333333333333\n  interception-job-1277.0,\"https://www.amtrak.com/services/MapDataService/AutoCompleterArcgis/getResponseList?searchTerm=boston\",2,9,0.6\n     │
│ interception-job-900.0,\"https://www.amtrak.com/services/globalerrorjson\",3,5,0.5\n  interception-job-1392.0,\"https://www.amtrak.com/services/globalerrorjson\",3,5,0.5\n  interception-job-1326.0,\"https://www.amtrak.com/tickets/departure.html\",3,3,0.30000000000000004\n                                         │
│ interception-job-935.0,\"https://www.amtrak.com/content/amtrak/en-us/home.alertsService.emergency-alerts.json\",2,2,0.13333333333333333\n  interception-job-996.0,\"https://ct.pinterest.com/user/?tid=2612534113654&cb=1769806170224&dep=2%2CPAGE_LOAD\",2,2,0.13333333333333333\n                                      │
│ interception-job-1465.0,\"https://ct.pinterest.com/user/?tid=2612534113654&pd=%7B%22pin_unauth%22%3A%22dWlkPU9UTm1ZemxpT1RBdE5XTmpNaTAwWkRJeExXRmxNakV0WldaallUTmhOMk00WmpKag%22%7D&cb=1769806190608&dep=2%2CPAGE_LOAD\",2,2,0.13333333333333333\n                                                                       │
│ interception-job-939.0,\"https://www.amtrak.com/content/amtrak/en-us/api/terms-and-conditions/version.model.json\",1,2,0.06666666666666667\n  interception-job-936.0,\"https://www.amtrak.com/content/amtrak/en-us/api/terms-and-conditions/version.model.json\",1,2,0.06666666666666667\n                               │
│ interception-job-1320.0,\"https://www.amtrak.com/dotcom/journey-solution-option\",1,1,0.03333333333333333"                                                                                                                                                                                                               │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

[2026-01-30 16:47:23] ERROR:bluebox.agents.network_spy:Error in agent loop: Error code: 400 - {'error': {'message': "Invalid type for 'messages[2].tool_calls[0].id': expected a string, but got null instead.", 'type': 'invalid_request_error', 'param': 'messages[2].tool_calls[0].id', 'code': 'invalid_type'}}
Traceback (most recent call last):
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/agents/network_spy.py", line 1057, in _run_agent_loop
    response = self._process_streaming_response(messages)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/agents/network_spy.py", line 1123, in _process_streaming_response
    for item in self.llm_client.call_stream_sync(
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/llms/llm_client.py", line 243, in call_stream_sync
    yield from self._client.call_stream_sync(
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/bluebox/llms/openai_client.py", line 706, in call_stream_sync
    stream = self._client.chat.completions.create(**kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/_utils/_utils.py", line 286, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/resources/chat/completions/completions.py", line 1192, in create
    return self._post(
           ^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1259, in post
    return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ray/Documents/code/Vectorly/bluebox-sdk/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1047, in request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid type for 'messages[2].tool_calls[0].id': expected a string, but got null instead.", 'type': 'invalid_request_error', 'param': 'messages[2].tool_calls[0].id', 'code': 'invalid_type'}}

⚠ Error: Error code: 400 - {'error': {'message': "Invalid type for 'messages[2].tool_calls[0].id': expected a string, but got null instead.", 'type': 'invalid_request_error', 'param': 'messages[2].tool_calls[0].id', 'code': 'invalid_type'}}

@rayruizhiliao
Copy link
Contributor

@claude

@claude
Copy link

claude bot commented Jan 30, 2026

Claude finished @rayruizhiliao's task in 1m 29s —— View job


Fixed 400 Error ✓

  • Read network_spy_agent.py to understand message handling
  • Identify root cause of null tool_calls[0].id
  • Implement fix
  • Commit and push changes

Root Cause

The 400 error occurred in bluebox/agents/network_spy_agent.py:640 when constructing messages for the OpenAI API. The code was using the wrong field name for tool calls.

Issue: The code used "call_id" as the field name, but the OpenAI API requires "id":

msg["tool_calls"] = [
    {
        "call_id": tc.call_id,  # ❌ Wrong - API expects "id"
        "name": tc.tool_name,
        "arguments": tc.tool_arguments,
    }
]

This caused the API to see null for the required tool_calls[0].id field, resulting in:

Error code: 400 - {'error': {'message': "Invalid type for 'messages[2].tool_calls[0].id': 
expected a string, but got null instead."}}

Fix Applied

Changed "call_id" to "id" in the tool_calls dictionary:

msg["tool_calls"] = [
    {
        "id": tc.call_id if tc.call_id else f"call_{idx}_{chat_id[:8]}",  # ✓ Correct
        "name": tc.tool_name,
        "arguments": tc.tool_arguments,
    }
]

The fix has been committed and pushed to the network-spy branch.


| Branch: network-spy

github-actions bot and others added 2 commits January 30, 2026 22:27
The OpenAI API expects tool_calls to have an 'id' field, not 'call_id'.
This was causing a 400 error: 'Invalid type for messages[2].tool_calls[0].id:
expected a string, but got null instead.'

Co-authored-by: Ruizhi (Ray) Liao <[email protected]>
@dimavrem22 dimavrem22 merged commit 3131b55 into main Jan 30, 2026
9 checks passed
@dimavrem22 dimavrem22 deleted the network-spy branch January 30, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants