test(host): pin the plain-JSON fold-state invariant on the contextHeaders unit

The headers fold helper now asserts every folded state is lossless JSON via
the framework's own snapshotJsonValue, matching the timeline specs; a new
hostile case covers the wrong-shaped plain JSON a committed log can hold and
pins verbatim survival of empty-object tool schemas. A comment on the
verbatim schema entry documents its session.append purification dependency.
This commit is contained in:
bowenliang123 committed 2026-08-29 18:57:54 +08:00
1 parent 2e54440297
commit a8cd10199e
2 files changed
+30

No files matched your search

+2
View File
@@ -68,6 +68,8 @@ function recordOf(event: SessionEvent): HeaderRecord | null {
const entry: HeaderTool = {
name: typeof tool.name === 'string' ? tool.name : '?',
tokens: estimateToolSchema(t),
// The raw entry rides state verbatim: committed event data is already the harness's
// lossless-JSON snapshot (session.append), so it can carry no non-JSON value.
schema: t,
}
if (typeof tool.description === 'string' && tool.description !== '') {
+28
View File
@@ -8,6 +8,7 @@ import { describe, test } from 'vitest'
import { createContextHeadersDefinition } from '../../src/host/headers'
import type { HeadersState } from '../../src/host/headers'
import { header, foreign } from './helpers/events'
import { assertPlainJson } from './helpers/projection'
import type { TimelineEvent } from '../../src/host/fold'
type Def = ReturnType<typeof createContextHeadersDefinition>
@@ -28,9 +29,11 @@ function headerEvent(seq: number, rawHeader: unknown): TimelineEvent {
return { type: 'request/header', seq, time: seq * 1000, data: { header: rawHeader, reason: 'initial' } }
}
/** Fold events through the unit, pinning the plain-JSON state precondition on every result. */
function fold(def: Def, events: TimelineEvent[]): HeadersState {
let state = def.init()
for (const ev of events) state = def.apply(state, ev as never)
assertPlainJson(state)
return state
}
@@ -48,6 +51,7 @@ describe('createContextHeadersDefinition', () => {
tools: [{ name: 'bash', description: 'run a command' }],
}) as never)
assert.notEqual(state, init, 'a header event produces a new state')
assertPlainJson(state)
assert.equal(state.headers.length, 1)
assert.equal(state.headers[0].seq, 1)
assert.equal(state.headers[0].system, 'You are an agent.')
@@ -228,4 +232,28 @@ describe('hostile tool entries', () => {
assert.ok('schema' in tool, 'the raw schema rides the record')
}
})
test('hostile shapes a committed log can hold keep the state lossless JSON', () => {
// Committed event data is already the harness's lossless-JSON snapshot (session.append
// throws on anything else), so the hostile surface here is wrong-SHAPED plain JSON —
// never functions, undefined-valued properties, or exotic objects.
const def = createContextHeadersDefinition()
const toolSchema = { name: 'bash', description: 'run a command', parameters: {} }
const state = fold(def, [headerEvent(1, {
tools: [
null,
42,
[],
{ name: 123, extra: { deep: [true, 'x'] } }, // wrong-typed name, nested junk
toolSchema, // an empty-object parameters schema is legal and must survive verbatim
{ name: 'read', parameters: { type: 'object', properties: {} } },
],
})])
assert.deepEqual(state.headers[0].tools[4].schema, toolSchema, 'a real ToolSchema entry rides verbatim')
assert.deepEqual(
state.headers[0].tools[5].schema,
{ name: 'read', parameters: { type: 'object', properties: {} } },
'nested empty-object schemas survive verbatim',
)
})
})