Merge remote-tracking branch 'origin/master' into codex/simp-prune-tools-prompt-surface
# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/services.md # packages/core/agent-loop/src/loop.ts # packages/core/session/README.md # packages/core/session/tests/derived-cache.spec.ts
This commit is contained in:
@@ -325,19 +325,17 @@ describe('HMR state rebuild', () => {
|
||||
it('rebuilds trace state for a session that exists at (re-)apply time', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
// First registration, mid-turn: a turn is open when the plugin reloads.
|
||||
const first = await ctx.plugin(Invariants)
|
||||
const session = ctx.sessions.create()
|
||||
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
await first.dispose()
|
||||
|
||||
// Re-apply (HMR): the fresh fiber must replay the existing log so the open
|
||||
// step is known — the next chunk must NOT be a false positive.
|
||||
// Re-apply mid-step: the new fiber must reconstruct the open boundaries from the log.
|
||||
await ctx.plugin(Invariants)
|
||||
expect(() => session.append('assistant/chunk', { turn: 1, step: 1, chunk: { type: 'text-delta', index: 0, text: 'h' } }))
|
||||
.not.toThrow()
|
||||
// And a genuine violation is still caught after the rebuild.
|
||||
// Rebuild must not disable later violations.
|
||||
expect(() => session.append('turn/start', { turn: 2, trigger: { kind: 'message', source: { kind: 'user' } } }))
|
||||
.toThrow(/turn 1 is still open/)
|
||||
})
|
||||
@@ -541,25 +539,17 @@ describe('surface invariants', () => {
|
||||
})
|
||||
|
||||
it('rejects sourceEventSeqs referencing unknown seq (gap in event log)', async () => {
|
||||
// The unknown-seq check fires when a ref passes the "earlier" test but is
|
||||
// not in knownSeqs — only possible with a gap in seqs. We create a gap by
|
||||
// directly manipulating the private log array to skip a seq.
|
||||
// Create an impossible-through-public-API gap so seq 2 is earlier but unknown.
|
||||
const { ctx } = await setup()
|
||||
const session = ctx.sessions.create()
|
||||
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||
session.append('step/start', { turn: 1, step: 1 })
|
||||
// Push a fake event at seq 3 into the internal log, creating a gap at seq 2.
|
||||
// The invariants plugin replays session.events on every append, so it sees
|
||||
// this gap during trace reconstruction.
|
||||
;(session as unknown as { log: unknown[] }).log.push({
|
||||
type: 'assistant/chunk',
|
||||
seq: 3,
|
||||
time: Date.now(),
|
||||
data: { turn: 1, step: 1, chunk: { type: 'text-delta', index: 0, text: 'x' } },
|
||||
})
|
||||
// Now the log has seqs 0, 1, 3 (gap at 2). Append at what session believes
|
||||
// is seq 3 (log.length). Reference seq 2: passes earlier (2 < 3) but not
|
||||
// in knownSeqs ({0, 1, 3} — gap at 2).
|
||||
expect(() => {
|
||||
session.append('assistant/message', { turn: 1, step: 1, content: [] }, { surfaceOp: 'append', sourceEventSeqs: [2] })
|
||||
}).toThrow(/unknown seq 2/)
|
||||
@@ -803,12 +793,8 @@ describe('request-reconstruction cross-check (llm/stream)', () => {
|
||||
|
||||
describe('request cross-check ordering (prepend)', () => {
|
||||
it('runs ahead of a short-circuiting llm/stream listener registered before it', async () => {
|
||||
// The replay adapter returns its chunks WITHOUT calling next(), which
|
||||
// would silence a later-registered check — snapshot compositions load
|
||||
// replay before the app bundle that loads invariants. The check prepends,
|
||||
// so it fires ahead of append-registered listeners regardless of load
|
||||
// order. (Prepend orders it against APPENDED listeners only; correctness
|
||||
// rests on the seq-bounded rebuild, not on listener timing.)
|
||||
// Replay short-circuits without next(), so the check prepends ahead of ordinary listeners;
|
||||
// correctness still comes from its sequence-bounded rebuild, not listener timing.
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
ctx.on('llm/stream', () => (async function* () {})() as never) // short-circuits, no next()
|
||||
|
||||
Reference in New Issue
Block a user