Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b8b0da5
fix(a2a): reject encoded responses before body reads
markstuart-oai Nov 5, 2026
02aba47
fix(a2a): preserve serialized structured results
markstuart-oai Nov 5, 2026
ce6124e
Read the realtime pairing test request count from the token server
markstuart-oai Nov 5, 2026
e1a40fe
Merge the SessionStore runtime and allow the API coverage fixture to …
markstuart-oai Nov 5, 2026
1e311e3
chore: merge Codex package extraction into A2A response guard
markstuart-oai Nov 5, 2026
d16fcb3
Integrate the owned-child tracing fix with the A2A adapter
markstuart-oai Nov 5, 2026
dfbee78
chore: integrate current child tracing policy into A2A response guard
markstuart-oai Nov 5, 2026
61d83aa
Integrate the macOS executor HTTP probe fix
markstuart-oai Nov 5, 2026
b5c2b46
Retain the native workflow policy through queued child submissions
markstuart-oai Nov 5, 2026
b182783
Use the owning turn for executor approval tracing
markstuart-oai Nov 5, 2026
f367048
Assert the denied child command stops before executor dispatch
markstuart-oai Nov 5, 2026
8206687
Exercise independent ambient and instrumented trace contexts
markstuart-oai Nov 5, 2026
48aa8a1
Integrate native child tracing with A2A structured results
markstuart-oai Nov 5, 2026
39806a9
Merge child tracing isolation repair into A2A response encoding
markstuart-oai Nov 5, 2026
44f6e78
Preserve child callback ownership across queued submissions and execu…
markstuart-oai Nov 5, 2026
64bc380
Integrate native child callback ownership with A2A structured results
markstuart-oai Nov 5, 2026
82d821a
Merge current child callback ownership integration
markstuart-oai Nov 5, 2026
b723a82
Keep W3C trace ancestry separate from workflow export settings
markstuart-oai Nov 5, 2026
f58b643
Integrate native trace-policy isolation with A2A structured results
markstuart-oai Nov 5, 2026
d5b24dc
Carry the admitted caller context through native child operations
markstuart-oai Nov 5, 2026
2fec101
Preserve admitted caller context for A2A structured results
markstuart-oai Nov 5, 2026
05c403a
Bind cancellation to the admitted turn context
markstuart-oai Nov 5, 2026
6fcf2b3
Verify export routing through the public tracing API
markstuart-oai Nov 5, 2026
6d01bd0
fix: wait for settled native child admission
markstuart-oai Nov 5, 2026
1875314
Merge commit 'b8a51306ccceea0da5553703fef14a5afe3f8fb4' into codex/v1…
markstuart-oai Nov 5, 2026
c5971ad
fix: integrate queued tracing into response encoding
markstuart-oai Nov 5, 2026
a560d1e
fix: integrate response encoding and queued tracing into structured r…
markstuart-oai Nov 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/a2a-serialized-results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@openai/agents': patch
'@openai/agents-codex': patch
---

fix: preserve the JSON representation of structured A2A results when saving and publishing a task.
6 changes: 6 additions & 0 deletions .changeset/limit-a2a-response-encoding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@openai/agents': patch
'@openai/agents-codex': patch
---

Reject compressed A2A discovery and RPC responses before reading their bodies, and request identity encoding so the adapter's response-byte bound does not depend on automatic decompression.
6 changes: 6 additions & 0 deletions .changeset/owned-child-tracing-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@openai/agents': patch
'@openai/agents-codex': patch
---

fix: preserve each queued native submission's trace and privacy settings when another workflow is active
28 changes: 17 additions & 11 deletions examples/realtime-react-native/test/token-server.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { once } from 'node:events';
import { request as httpRequest } from 'node:http';
import { createServer } from 'node:net';
import type { AddressInfo } from 'node:net';
Expand Down Expand Up @@ -28,8 +29,10 @@ async function startServer(host: string, configuredPort?: number) {
return listen.call(this, ${port}, ...args);
};`
}
let upstreamRequests = 0;
process.on('message', () => process.send(upstreamRequests));
globalThis.fetch = async () => {
console.log('UPSTREAM_REQUEST');
upstreamRequests++;
return new Response(JSON.stringify({ value: 'test-client-secret', expires_at: 123 }), { status: 200 });
};
await import(${JSON.stringify(serverUrl)});
Expand All @@ -41,7 +44,7 @@ async function startServer(host: string, configuredPort?: number) {
PORT: String(configuredPort ?? port),
OPENAI_API_KEY: 'test-api-key',
},
stdio: ['ignore', 'pipe', 'pipe'],
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
});
let output = '';
await new Promise((resolve, reject) => {
Expand All @@ -63,8 +66,11 @@ async function startServer(host: string, configuredPort?: number) {
});
return {
port,
get output() {
return output;
async upstreamRequests() {
const reply = once(child, 'message');
child.send('upstreamRequests');
const [count] = await reply;
return count as number;
},
pairingToken: output.match(/Pairing token: ([A-Za-z0-9_-]+)/)?.[1],
request: (headers?: Record<string, string>) =>
Expand Down Expand Up @@ -95,7 +101,7 @@ test('LAN token issuance requires pairing and limits each client', async () => {
(await server.request({ Authorization: 'Bearer wrong' })).status,
401,
);
assert.equal(server.output.includes('UPSTREAM_REQUEST'), false);
assert.equal(await server.upstreamRequests(), 0);

assert.ok(server.pairingToken);
const headers = { Authorization: `Bearer ${server.pairingToken}` };
Expand All @@ -105,7 +111,7 @@ test('LAN token issuance requires pairing and limits each client', async () => {
assert.equal((await response.json()).value, 'test-client-secret');
}
assert.equal((await server.request(headers)).status, 429);
assert.equal(server.output.match(/UPSTREAM_REQUEST/g)?.length, 6);
assert.equal(await server.upstreamRequests(), 6);
} finally {
server.stop();
}
Expand Down Expand Up @@ -137,7 +143,7 @@ test('loopback token issuance requires pairing and an expected origin', async ()
try {
assert.ok(server.pairingToken);
assert.equal((await server.request()).status, 401);
assert.equal(server.output.includes('UPSTREAM_REQUEST'), false);
assert.equal(await server.upstreamRequests(), 0);

const headers = { Authorization: `Bearer ${server.pairingToken}` };
assert.equal(
Expand All @@ -162,15 +168,15 @@ test('loopback token issuance requires pairing and an expected origin', async ()
).status,
403,
);
assert.equal(server.output.includes('UPSTREAM_REQUEST'), false);
assert.equal(await server.upstreamRequests(), 0);

assert.equal((await server.request(headers)).status, 200);
assert.equal(
(await server.request({ ...headers, Host: `10.0.2.2:${server.port}` }))
.status,
200,
);
assert.equal(server.output.match(/UPSTREAM_REQUEST/g)?.length, 2);
assert.equal(await server.upstreamRequests(), 2);
} finally {
server.stop();
}
Expand Down Expand Up @@ -204,7 +210,7 @@ test('HTTP port 80 accepts exact portless aliases and serialized origins while r
).status,
403,
);
assert.equal(server.output.includes('UPSTREAM_REQUEST'), false);
assert.equal(await server.upstreamRequests(), 0);

for (const alias of ['127.0.0.1', 'localhost', '10.0.2.2']) {
for (const Host of [alias, `${alias}:80`]) {
Expand All @@ -221,7 +227,7 @@ test('HTTP port 80 accepts exact portless aliases and serialized origins while r
(await server.request({ ...headers, Host: 'localhost' })).status,
429,
);
assert.equal(server.output.match(/UPSTREAM_REQUEST/g)?.length, 6);
assert.equal(await server.upstreamRequests(), 6);
} finally {
server.stop();
}
Expand Down
4 changes: 2 additions & 2 deletions packages/agents-codex/A2A.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ Discovery sends no credentials. After the card passes admission, the adapter res

The returned client is the official A2A `Client`. Direct callers must treat an uncertain mutation acknowledgment as an unknown outcome; message IDs do not make creation idempotent. This profile supports `sendMessage`, `getTask`, and `cancelTask`. It uses polling rather than streaming, push notifications, extended cards, or remote callback execution. Native input supports text and inline PNG, JPEG, GIF, and WebP images. File URLs, arbitrary URIs, and other file types fail before native admission. Result validation remains the application's responsibility.

`A2ALimits` sets `bodyBytes`, `inputBytes`, `checkpointBytes`, and `timeoutMs`. Defaults are 2 MiB, 1 MiB, 8 MiB, and 30 seconds. The HTTP reader stops before buffering more than `bodyBytes`. These bounds are adapter defaults, not service quotas. The host should also bound incoming HTTP bodies before protocol parsing.
`A2ALimits` sets `bodyBytes`, `inputBytes`, `checkpointBytes`, and `timeoutMs`. Defaults are 2 MiB, 1 MiB, 8 MiB, and 30 seconds. The adapter requests identity HTTP encoding and cancels responses that declare another encoding before reading their bodies. `bodyBytes` limits the chunks the adapter retains. A custom `fetch` must preserve response encoding headers and body cancellation. The transport controls its own internal buffers. These bounds are adapter defaults, not service quotas. The host should also bound incoming HTTP bodies before protocol parsing.

## Deferred remote work

`A2AJobProvider` implements the existing `JobProvider` contract. Register it with `DeferredTools`, a host `JobJournal`, and a current result projection. Set `requireReconciliation: false` only for work whose uncertain outcome the host can handle. Generic A2A cannot prove whether a lost create was accepted.

The provider requires separate callbacks for current authorization, credential resolution, durable mutation reservation, input encoding, and result validation. Authorization checks the current principal, immutable native invocation binding, and claim generation. After discovery and local validation, the provider resolves credentials, checks authorization again, spends one mutation reservation, and sends once. A timeout or lost create, input-reply, or cancel acknowledgment returns `unknown`. It never resends that mutation. A known receipt binds the endpoint and original native invocation. A fresh worker observes that same remote task with `getTask`; it does not create another task.

`inputRequired` exposes a remote challenge. `A2AInputReply` supplies its task ID, context ID, challenge, generation, and JSON `result`. The provider reads the current remote task before reserving a reply. Python and TypeScript share one wire profile: the status message contains one data part `{challenge, generation, tool, arguments}`; a reply contains exactly `{challenge, generation, result}`. Task and context IDs belong in the A2A message envelope. Structured completion artifacts contain `{result: output}`; string results remain text parts. `authRequired` remains a host credential flow and never becomes a generic approval answer. A host must not accept untrusted remote approval text as authority to perform local actions.
`inputRequired` exposes a remote challenge. `A2AInputReply` supplies its task ID, context ID, challenge, generation, and JSON `result`. The provider reads the current remote task before reserving a reply. Python and TypeScript share one wire profile: the status message contains one data part `{challenge, generation, tool, arguments}`; a reply contains exactly `{challenge, generation, result}`. Task and context IDs belong in the A2A message envelope. The executor publishes string results as text parts. For structured results, it saves and sends `{result: output}` using the JSON representation that passed the byte limit. `authRequired` remains a host credential flow and never becomes a generic approval answer. A host must not accept untrusted remote approval text as authority to perform local actions.

## Serve a native agent

Expand Down
Loading
Loading