Skip to content

feat: count shell fetches as docs activation, read Codex's web_search action - #130

Open
mattrossman wants to merge 20 commits into
mainfrom
mattrossman/ai-958-investigate-incomplete-codex-web-search-docs-activation
Open

feat: count shell fetches as docs activation, read Codex's web_search action#130
mattrossman wants to merge 20 commits into
mainfrom
mattrossman/ai-958-investigate-incomplete-codex-web-search-docs-activation

Conversation

@mattrossman

@mattrossman mattrossman commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Changes how we track docs activation for Codex:

  • stdout-facing shell commands like curl and wget -O - are now counted as docs calls (with content)
  • The web_search tool's action payload is now used to determine whether the model opened a page, instead of inferring this based on a URL-shaped query
  • Docs calls where hasContent is undefined are rendered with a distinct question mark instead of falling back to looking like content-ful calls

Example of the new SHELL docs activation tracking in the "cli 001: bootstrap app" eval run from the preview app:

CleanShot 2026-07-28 at 17 40 55@2x

Another example where the hasContent couldn't be determined:

CleanShot 2026-07-28 at 17 40 47@2x

Motivation

Our skill tells agents to fetch https://supabase.com/changelog.md and in several of our runs, Codex achieves this with curl which we're not currently counting. For comparison, Claude Code uses WebFetch which is counted.

This change aims to more accurately count changelog reads, among other docs pages.

The web_search change is less important, but still a technical accuracy thing for how we count page reads in Codex.

What's counted

This only counts commands that write page content to stdout, since we can generally trust that means the content was added to context. It counts supabase.com domains only, excluding service subdomains. resultChars is measured after whatever the agent piped the response through, so it counts what reached the model rather than the size of the whole page. If the command has ambiguous output, we treat hasContent as undefined.

It doesn't count commands that don't direct to stdout (e.g. downloading a page and then reading it from filesystem). The reasoning is that it's harder to reliably track this, as there's tons of ways that our heuristics might be inaccurate. We also don't see evidence of this happening in practice to justify the effort. We can revisit that sort of tracking once we notice it impacting results.

See tests cases for more context on what's counted vs excluded.

Impact on transcript and UI

This adds a shell_fetch type to DocsCall.source similar to existing MCP seach_docs and harness web_fetch and web_search sources.

In this UI this appears as a SHELL chip tagged on the docs call. I label it as such since it represents multiple possible shell fetch strategies, but if desired we could split into the specific binary like CURL, WGET, etc.

Shell invocations that mix multiple commands w/o piping are counted as docs calls but with hasContent undefined, since we can't be sure what content was printed to stdout as context. Those now render distinctly in the UI from known content-ful or content-less calls.

On web_search.action change

Note that although this PR starts reading Codex's web_search.action payload, there's a bug in Codex preventing this helping to its full potential. Similar to this fixed issue, there's inconsistency in the action casing between validation (camelCase vs. snake_case) that causes the open_page action (or any multi-word action) to fall back to other.

You can see this in practice by running:

codex exec --json --ephemeral --skip-git-repo-check \
  'Use web search to open https://raw.githubusercontent.com/openai/codex/main/CHANGELOG.md directly. Do not use curl or shell commands.' \
  | jq -c 'select(.item.type == "web_search") | {query: .item.query, action: .item.action}'

We can consider opening an issue in the Codex repo, and for now this change is a guard for if/when that validation issue gets fixed.

Nonetheless, the other action falls back to the existing URL-shaped query heuristic, so it's not regressing our results.

I've updated results for the Codex experiments on this PR, I didn't refresh other harnesses since I haven't observed them using shell to fetch web pages thusfar.

Closes AI-958

@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
evals Ignored Ignored Preview Jul 31, 2026 6:18pm

Request Review

@linear-code

linear-code Bot commented Jul 27, 2026

Copy link
Copy Markdown
AI-958 Investigate incomplete Codex web search / docs activation results

Motivation: Changelog content activation is suspiciously low.

Initial research suggests that Codex's web search tool masks which underlying pages it read content from which might be under-representing it in the content-ful docs activation results.

Review in Linear

@mattrossman mattrossman changed the title feat: count shell fetches and read Codex's web_search action for docs activation feat: count shell fetches as docs activation, read Codex's web_search action Jul 28, 2026
@mattrossman
mattrossman marked this pull request as ready for review July 28, 2026 19:19
@mattrossman
mattrossman requested a review from a team July 28, 2026 19:19

@Rodriguespn Rodriguespn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. We discussed offline the details of this so I'm in the loop. Nice catch and thkx for taking care of this

@barryroodt barryroodt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of completeness, I had my agent run a thorough review on this, all feedback below, along with the inline comments, is a result of that run.

Nice direction, though the shell heuristic still miscounts a few common cases, all in the direction that moves the number this PR exists to make accurate.

  • echo "run curl https://supabase.com/changelog.md next" records a page read with hasContent: true. The word curl anywhere in the segment is enough.
  • curl -fsSL <changelog> 2>/dev/null | head -50 records nothing at all.
  • A 404 counts as a read, since curl without -f exits 0 and the error body lands in result.

Left those inline with the details. A regression test for each would be the useful part, more than the specific fix.

Separately on the results: they look re-run rather than re-scored. 71 of 190 rows differ, 53 of those changed a run-level field like attempts or checks, search_docs moved 146 to 142 even though this PR doesn't touch that branch, and 7 rows flipped pass/fail. Nothing wrong with re-running, just worth a line in the description so nobody reads those flips as effects of this change.

One structural thing I'd like your take on, and I think it's fine as a follow-up: the Codex action handling lives in docs-results.ts even though that module is meant to stay agent-agnostic.

Comment thread packages/core/src/docs-results.ts Outdated
Comment thread packages/core/src/docs-results.ts Outdated
Comment thread packages/core/src/docs-results.ts
Comment thread packages/core/src/docs-results.ts
Comment thread packages/core/src/docs-results.ts Outdated
Comment thread packages/core/src/docs-results.ts
Comment thread packages/core/src/docs-results.ts
Comment thread packages/core/src/eval-metadata.ts
@mattrossman

Copy link
Copy Markdown
Collaborator Author

Separately on the results: they look re-run rather than re-scored

Yep, that's just a byproduct of refreshing results on the PR w/ nondeterminism. All good.

@mattrossman

Copy link
Copy Markdown
Collaborator Author

the Codex action handling lives in docs-results.ts even though that module is meant to stay agent-agnostic.

Agreed it's not ideal, I'm okay leaving it for now because this doesn't affect the persisted results shape, and can be simplified by using the existing normalized url once the casing bug on Codex's end is resolved.

…vestigate-incomplete-codex-web-search-docs-activation

# Conflicts:
#	apps/web/src/data/eval-results.json
A curl without -f/--fail exits 0 on a 4xx/5xx, so its output landing in
`result` doesn't prove the page was actually read. wget doesn't need the
same check since it already fails on HTTP errors by default.
isSupabaseUrl (any *.supabase.com subdomain) and isSupabaseDocsUrl (apex host only) disagreed on whether a service subdomain like mcp.supabase.com counts as a read, depending on which tool channel reported it. Consolidate on the stricter apex-only check everywhere.
Keeps scorers/judges seeing the same truncated result the agent did,
instead of the full rehydrated content meant only for docs metrics.
Also checks the truncation stub before running the shell-fetch regex
scan, skipping that work for the common non-truncated case.
A stderr-only redirect (2>/dev/null, 2>&1) was tripping the same
"output redirected" check as a real stdout redirect, dropping curl
fetches like `curl ... 2>/dev/null | head` entirely even though the
page body still reaches the model.
Matching /\bcurl\b/ anywhere in a shell segment counted a mere mention
of the word (e.g. `echo curl https://supabase.com/changelog.md`) as a
page read. Anchors curl/wget to the segment's leading command instead,
unwrapping `bash/sh/zsh -c "..."` wrappers first so a fetch piped
inside one of those still resolves correctly.
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.

3 participants