Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks

# Conflicts:
#	docs/event-producer-consumer.md
This commit is contained in:
Yichen Jiang
2026-07-15 13:30:39 +08:00
72 changed files with 730 additions and 555 deletions

View File

@@ -578,7 +578,8 @@ async function runStep(
})
session.append('tool/result', {
turn, step,
// Preserve transcript pairing even if a post-execute listener returns another id.
// Correlation comes from the immutable execution input; the result does
// not duplicate this authoritative transcript identity.
callId: call.id,
content: result.content,
isError: result.isError,

View File

@@ -1043,8 +1043,7 @@ describe('tool result call identity', () => {
// A post-execute listener transforms the result (accept-with-replacement).
// The loop must still record the tool/result under the model's authoritative
// call.id (the loop ignores result.callId — which the registry always sets to
// exec.callId anyway — and uses call.id, the model-transcript id).
// call.id, which is the immutable identity carried by the execution input.
ctx.on('tools/post-execute', (exec, _result) => {
expect(exec.callId).toBe(CallId('c1')) // the loop passed the real id in
return Promise.resolve({ kind: 'accept', content: [{ type: 'text', text: 'ok' }] })
@@ -1054,8 +1053,7 @@ describe('tool result call identity', () => {
send(agent, 'use tool')
await waitForIdle(ctx, agent)
// The logged tool/result.callId is the originating call.id, NOT the
// listener's wrong id.
// The logged tool/result.callId is the originating call.id.
const resultEvent = [...agent.session.events].find(e => e.type === 'tool/result')
expect(resultEvent?.type).toBe('tool/result')
if (resultEvent?.type === 'tool/result') {