openai / openai/codex

Preserve Programmatic Tool Calling caller linkage in App Server and rollouts

Open
#40,749 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app-server enhancement
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

Summary

Codex currently drops the Responses API Programmatic Tool Calling parent relationship before App Server and rollout consumers can observe it. Preserve the upstream program / nested call / result / program_output relationship so clients can measure real nested tool execution without treating source text or temporal containment as runtime call identity.

Current observed boundary

I checked Codex CLI 0.149.1 and public main at dc08ace7821614a702b1214c9d08ae0db2634d82.

  • codex app-server generate-json-schema --experimental exposes call_id on raw function_call, function_call_output, custom_tool_call, and custom_tool_call_output response items.
  • The generated ResponseItem union has no program or program_output variant and those call/result variants have no caller field.
  • v2 ThreadItem call variants such as commandExecution, fileChange, and dynamicToolCall have a presentation id, status, and some duration fields, but no stable nested callId or parent caller.
  • item/started and item/completed already carry startedAtMs and completedAtMs respectively, so lifecycle timing exists but cannot be joined into the programmatic caller graph.
  • A real Codex rollout containing functions.exec programmatic calls persisted the outer custom_tool_call / custom_tool_call_output and independently observed command/edit items, but no raw program, program_output, or nested caller-linked lifecycle rows.

This is not a claim that the current App Server documentation promises those fields. It is a request to preserve the separate, documented Responses API Programmatic Tool Calling relationship through Codex's protocol and persistence surfaces.

Upstream contract

The official Programmatic Tool Calling guide defines:

  • a program item with its own call_id;
  • program-issued calls with their own call_id and caller: { type: "program", caller_id: <program call id> };
  • caller preservation when returning the matching call output; and
  • program_output.call_id matching the program, with terminal status.

Guide: https://developers.openai.com/api/docs/guides/tools-programmatic-tool-calling

The App Server guide defines item/started and item/completed, with the completed item authoritative:

https://developers.openai.com/codex/app-server

Requested Codex behavior
  1. Extend the core raw response model so program and program_output round-trip without loss, and program-issued function/custom calls and outputs retain their optional caller { type, caller_id }.
  2. Preserve those raw items unchanged in rollout JSONL and rawResponseItem/completed notifications.
  3. Add stable callId plus optional caller { type, callerId } to App Server ThreadItem call variants. For programmatic calls, emit the same identifiers on both item/started and item/completed.
  4. Keep presentation id, lifecycle join callId, and parent join callerId distinct.
  5. Preserve started nested calls across failure, interruption, cancellation, and missing-result cases. Allow a nested call to be the caller of another call so the graph has arbitrary depth.
  6. Represent terminal state explicitly (completed, failed, cancelled/interrupted, or the existing equivalent) and keep the existing lifecycle timestamps.

Equivalent names are fine if the protocol owns a different canonical vocabulary; the required information and stable joins are the contract.

Acceptance evidence
  • Generated JSON Schema and TypeScript include the raw program / program_output variants and caller fields, plus caller linkage on relevant ThreadItem variants.
  • Serialization round-trip tests prove caller preservation on both calls and outputs.
  • App Server integration tests cover sequential siblings, parallel siblings, depth greater than one, failure, cancellation/interruption, and a started call with no result.
  • A real rollout proves the same stable nested callId and callerId on lifecycle start/completion, while the outer orchestration envelope remains distinguishable from its nested operations.

Without those fields, consumers can still report independently observed commands and edits as unlinked lower-bound evidence, but cannot honestly claim complete nested-call attribution, depth, concurrency, or inclusive nested operational duration.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the core raw response model, generated JSON Schema and TypeScript unions, then trace App Server ThreadItem definitions, rollout JSONL, and rawResponseItem/completed notifications. Compare these surfaces with the linked Programmatic Tool Calling guide and existing lifecycle behavior; done means round-trip preservation and integration evidence for nested, parallel, failed, cancelled, and result-less calls.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, typescript
Domain
api, backend, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.