Merge origin/master into codex/send-one-turn
# Conflicts: # docs/cordis-catalog/events.md # docs/event-producer-consumer.md # docs/persistence-catalog.md # packages/core/agent-loop/src/loop.ts # packages/core/agent/README.md # website/zh-CN/api/harness/events.md
This commit is contained in:
@@ -31,7 +31,7 @@ Each non-empty terminal line outside an active question becomes one text block,
|
||||
|
||||
#### Token effect
|
||||
|
||||
Submitted text is retained under the agent loop's normal session-history and compaction rules. The welcome banner, `> ` prompt, rendered transcript, and `[tool call]` / `[tool result]` terminal lines add no tokens.
|
||||
Submitted text is retained under the agent loop's normal session-history and compaction rules. The welcome banner, `> ` prompt, rendered transcript, and `[tool call]` / `[tool result]` terminal lines add no tokens. A replacement `tool/result` remains model-visible through the session surface but is not rendered as a second execution; stdio keeps the original full-fidelity result line.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
|
||||
@@ -145,6 +145,10 @@ export function createStdioChat(ctx: Context, config: Config, runtime: StdioRunt
|
||||
inReasoning = false
|
||||
output.write(`\n [tool call] ${toolName}(${args})`)
|
||||
} else if (event.type === 'tool/result') {
|
||||
// A surface replacement changes future model context; it is not another
|
||||
// execution. Keep the original full-fidelity terminal presentation and
|
||||
// suppress duplicate output during live delivery or log replay.
|
||||
if (event.surfaceOp !== undefined && event.surfaceOp !== 'append') return
|
||||
const { content } = event.data
|
||||
const text = content.filter(block => block.type === 'text').map(block => block.text).join('')
|
||||
output.write(`\n [tool result] ${text}\n `)
|
||||
|
||||
@@ -367,6 +367,43 @@ describe('createStdioChat rendering', () => {
|
||||
expect(out.text()).toContain('[tool result] file.txt')
|
||||
})
|
||||
|
||||
it('renders one full-fidelity result whether the event feed is live or replayed', async () => {
|
||||
const { ctx, out } = await setup()
|
||||
const session = makeSession('main')
|
||||
const original = {
|
||||
type: 'tool/result',
|
||||
seq: 2,
|
||||
time: 0,
|
||||
data: {
|
||||
turn: 1,
|
||||
step: 1,
|
||||
callId: 'c1',
|
||||
content: [{ type: 'text', text: 'full terminal output' }],
|
||||
isError: false,
|
||||
meta: { terminal: { output: 'full terminal output' } },
|
||||
},
|
||||
surfaceOp: 'append',
|
||||
} as SessionEvent
|
||||
const replacement = {
|
||||
...original,
|
||||
seq: 3,
|
||||
data: {
|
||||
...original.data,
|
||||
content: [{ type: 'text', text: '[... tool result middle pruned ...]' }],
|
||||
},
|
||||
surfaceOp: { op: 'replace', start: 2, end: 2 },
|
||||
sourceEventSeqs: [2],
|
||||
} as SessionEvent
|
||||
|
||||
// Stdio consumes the same session/event shape whether a host forwards a
|
||||
// live append or replays a stored log through the rendering feed.
|
||||
for (const event of [original, replacement]) ctx.emit('session/event', session, event)
|
||||
|
||||
expect(out.text().match(/\[tool result\]/g)).toHaveLength(1)
|
||||
expect(out.text()).toContain('full terminal output')
|
||||
expect(out.text()).not.toContain('tool result middle pruned')
|
||||
})
|
||||
|
||||
it('renders a todo/write session event as a glyphed checklist', async () => {
|
||||
const { ctx, out } = await setup()
|
||||
const session = {} as Session
|
||||
|
||||
Reference in New Issue
Block a user