fix(sensor): extract custom_tool_call tool usage from Codex sessions - #34
Merged
Conversation
Codex Desktop emits agent tool invocations as `custom_tool_call` /
`custom_tool_call_output` response items, which CodexParser did not
recognise. Affected sessions parsed successfully and exported as healthy
events while every tool call in them was silently discarded, so a session
that ran five `exec` calls was indistinguishable from a plain chat.
Three shape differences had to be handled together:
- record type: `custom_tool_call` is now matched alongside `function_call`
- arguments: carried as a raw, frequently non-JSON string under `input`
rather than a JSON string under `arguments`. Matching only on the type
would have yielded tools with empty arguments, which for a shell
execution tool discards the entire signal
- output: a list of `{type, text}` content items rather than a plain
string, so it bypassed truncation and placed a list in a str field
Tool status is now taken from the record instead of being hardcoded, and
the response-item type is recorded as `tool_type` so downstream rules can
distinguish agent tool calls from classic function calls.
Verified against a real Codex Desktop session: 5 tools recovered with
arguments and results intact, previously 0.
pengyuzhang
force-pushed
the
fix/codex-custom-tool-call
branch
from
August 9, 2026 20:43
78b175d to
a064784
Compare
|
thenk you |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Codex Desktop emits agent tool invocations as
custom_tool_call/custom_tool_call_outputresponse items.CodexParseronly recognisedfunction_call/function_call_output, so those records were dropped.The failure was silent rather than loud: affected sessions parsed successfully,
has_meaningful_content()returnedTrue, and the event exported with its conversation text intact — while every tool call in it was discarded. A session that ran fiveexeccalls was indistinguishable from a plain chat.For a sensor whose detections key on tool arguments (credential exfiltration, data movement), losing the command string loses the entire signal.
Root cause
Three shape differences that have to be handled together — fixing only the first yields tools with empty arguments, which is arguably worse than dropping them because it looks like coverage:
function_callcustom_tool_callfunction_callcustom_tool_callargumentsinput{type, text}content itemsThe list-shaped output also bypassed the
isinstance(output, str)truncation guard and placed alistintoToolUsage.result, which is typedOptional[str].Changes
custom_tool_call/custom_tool_call_outputalongside the existing types_parse_tool_arguments()— coerces the argument payload to a dict, falling back to{"raw": ...}so a non-JSON command string is preserved rather than discarded_normalize_tool_output()— flattens a list of content items to text before truncating, so list outputs are capped like every other resultstatusfrom the record instead of hardcodingpending→successtool_typeso downstream rules can distinguish agent tool calls from classic function callsNo schema change. No new source.
function_callbehaviour is unchanged and pinned by a regression test.Verification
Real data —
adr-sensor --source codexend-to-end over 16 local sessions:+35 recovered (
custom_tool_call: 35,function_call: 211); 243 of 246 carry non-empty arguments. The 3 without are legitimately argument-less (list_mcp_resources,list_mcp_resource_templates,list_available_plugins_to_install).Unit — 9 new tests in
TestCodexParser, fixtures synthetic per CONTRIBUTING but every field shape copied from real session records:input, list output) → name, args, joined result, statusinputparses to a dict rather than{"raw"}function_callunchanged (regression guard)pendingcall_idinvents no toolevent_msgrecords skipped cleanly (currently unparsed — see below)Suite: 114 → 123 passing.
ruff checkclean on the changed module.Not addressed here
Found while investigating, left for separate PRs:
event_msgrecords are unparsed — they carryweb_search_end(query +call_id, an egress signal),token_count(total_token_usage,model_context_window), anduser_message.imagesCodexParseris the only parser that ignoresmax_age_days, so it globs every session ever written and--all-historyis a no-op for it~/.codex/archived_sessions/is a sibling ofsessions/and falls outside the glob