From febafc7ee0519b855d26db28c2d48371c2d3ba2e Mon Sep 17 00:00:00 2001 From: _Kerman Date: Mon, 31 Aug 2026 17:43:17 +0800 Subject: [PATCH] fix(test): cover indexed session reads --- packages/acp/acp/tests/approval.spec.ts | 5 ++++- packages/bundle/headless/tests/headless.spec.ts | 15 +++++++++++++++ .../agent-loop/tests/contract-regressions.spec.ts | 2 +- packages/fs/tool-fs/tests/tools.spec.ts | 10 ++++++++-- .../session-log-deepseek/tests/upload.spec.ts | 11 +++++++++++ packages/shell/tool-bash/tests/tools.spec.ts | 11 +++++++---- packages/shell/tool-pwsh/tests/tools.spec.ts | 15 ++++++++++++--- .../skill/tool-skill/tests/tool-skill.spec.ts | 12 ++++++++++++ 8 files changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/acp/acp/tests/approval.spec.ts b/packages/acp/acp/tests/approval.spec.ts index 7ac71f1ea0..25fc940a06 100644 --- a/packages/acp/acp/tests/approval.spec.ts +++ b/packages/acp/acp/tests/approval.spec.ts @@ -68,10 +68,13 @@ describe('ACP machine permission policy', () => { it('delegates a same-id foreign agent', async () => { harness = await makeBridgeHarness() const request = await ownedRequest() + const events = [{ type: 'turn/start', seq: 0, time: 0, data: { turn: 1 } }] const foreign = { session: { id: request.agent.session.id, - snapshotEvents: () => [{ type: 'turn/start', seq: 0, time: 0, data: { turn: 1 } }], + seq: events.length, + eventAt: (seq: number) => events[seq], + snapshotEvents: () => events, append: () => ({}), }, } as unknown as Agent diff --git a/packages/bundle/headless/tests/headless.spec.ts b/packages/bundle/headless/tests/headless.spec.ts index fb86ff8387..bc788682bf 100644 --- a/packages/bundle/headless/tests/headless.spec.ts +++ b/packages/bundle/headless/tests/headless.spec.ts @@ -310,6 +310,21 @@ describe('headless runner', () => { await test.ctx.fiber.dispose() }) + it('fails when an event below the captured Session length cannot be read', async () => { + const test = await bench({ + afterPrompt(session, message) { + appendTurn(session, 1, message, 'unreachable', true) + Object.defineProperty(session, 'eventAt', { value: () => undefined }) + }, + }) + expect(await test.run()).toMatchObject({ + code: 1, + out: '', + err: 'dsh: headless summary cannot read seq 0 below captured length 7\n', + }) + await test.ctx.fiber.dispose() + }) + it('reports a direct Agent creation failure', async () => { const ctx = new Context() let err = '' diff --git a/packages/core/agent-loop/tests/contract-regressions.spec.ts b/packages/core/agent-loop/tests/contract-regressions.spec.ts index 4774099847..ab338d3b01 100644 --- a/packages/core/agent-loop/tests/contract-regressions.spec.ts +++ b/packages/core/agent-loop/tests/contract-regressions.spec.ts @@ -540,7 +540,7 @@ describe('adapter registration, routing, and accepted-input ownership', () => { send(agent, 'go') await waitForIdle(ctx, agent) - const events = agent.session.events + const events = agent.session.snapshotEvents() const claims = events.flatMap(event => event.type === 'agent/inbox/spliced' && event.data.target === 'next-step' && event.data.outcome !== 'canceled' diff --git a/packages/fs/tool-fs/tests/tools.spec.ts b/packages/fs/tool-fs/tests/tools.spec.ts index ecd258f24c..bb14264f6d 100644 --- a/packages/fs/tool-fs/tests/tools.spec.ts +++ b/packages/fs/tool-fs/tests/tools.spec.ts @@ -806,12 +806,18 @@ describe('sandbox escalation API (write/edit)', () => { /** A fake agent whose session records appends (the approval audit trail), mid-turn, carrying the given events for the fold. */ function escalationAgent(events: Array<{ type: string; data?: Record }> = []): object { + const log = [ + { type: 'turn/start', data: { turn: 1 }, seq: 0 }, + ...events.map((event, index) => ({ ...event, seq: index + 1 })), + ] return { id: 'agent-fs-esc', session: { header: { version: 0, id: 'sess-fs-esc', createdAt: 0, cwd: '/session-project' }, - snapshotEvents: () => [{ type: 'turn/start', data: { turn: 1 } }, ...events], - append: (type: string, data: Record) => { events.push({ type, data }) }, + get seq() { return log.length }, + eventAt: (seq: number) => log[seq], + snapshotEvents: () => log, + append: (type: string, data: Record) => { log.push({ type, data, seq: log.length }) }, }, } } diff --git a/packages/session/session-log-deepseek/tests/upload.spec.ts b/packages/session/session-log-deepseek/tests/upload.spec.ts index 560851a4d8..38526420e0 100644 --- a/packages/session/session-log-deepseek/tests/upload.spec.ts +++ b/packages/session/session-log-deepseek/tests/upload.spec.ts @@ -133,6 +133,17 @@ describe('incremental DeepSeek session-log upload', () => { expect(reads).toBe(2) }) + it('rejects a missing event below the captured Session length', () => { + const session = { + id: SessionId('missing-event'), + seq: 1, + eventAt: () => undefined, + } as unknown as Session + + expect(() => SessionLogDeepSeek.acceptedThrough(session)) + .toThrow('session-log-deepseek: missing event 0 below captured length 1') + }) + it('omits the field for direct or stale requests and uploads the prior acceptance marker next', async () => { const { ctx, session } = await harness('edges') await expect(ctx.deepseekLlmApiExtensions.prepare({ body: body(), signal: SIGNAL })) diff --git a/packages/shell/tool-bash/tests/tools.spec.ts b/packages/shell/tool-bash/tests/tools.spec.ts index 41ff321721..fd6bc51b16 100644 --- a/packages/shell/tool-bash/tests/tools.spec.ts +++ b/packages/shell/tool-bash/tests/tools.spec.ts @@ -205,8 +205,8 @@ function sandboxAgent( ctx?: Context, onAppend?: (type: string) => void, ): Agent { - const events: Array<{ type: string; data?: Record }> = [{ type: 'turn/start', data: { turn: 1 } }] - if (mode !== undefined) events.push({ type: 'sandbox/mode', data: { mode } }) + const events: Array<{ type: string; data?: Record; seq: number }> = [{ type: 'turn/start', seq: 0, data: { turn: 1 } }] + if (mode !== undefined) events.push({ type: 'sandbox/mode', seq: events.length, data: { mode } }) const id = SessionId('sandbox-session') return { id, @@ -214,9 +214,11 @@ function sandboxAgent( session: { id, header: { version: 0, id, createdAt: 0 }, + get seq() { return events.length }, + eventAt: (seq: number) => events[seq], snapshotEvents: () => events, append: (type: string, data: Record) => { - const event = { type, data } + const event = { type, data, seq: events.length } events.push(event) onAppend?.(type) return event @@ -625,9 +627,10 @@ describe('sandbox escalation through the generic task producer', () => { expect(prompted).not.toHaveBeenCalled() const malformed = sandboxAgent() - ;(malformed.session.snapshotEvents() as unknown as Array<{ type: string; data: { mode: string } }>).push({ + ;(malformed.session.snapshotEvents() as unknown as Array<{ type: string; data: { mode: string }; seq: number }>).push({ type: 'sandbox/mode', data: { mode: 'unknown-mode' }, + seq: malformed.session.seq, }) expect(text(await call(ctx, 'bash', escalate, malformed))).toContain('not strictly wider') }) diff --git a/packages/shell/tool-pwsh/tests/tools.spec.ts b/packages/shell/tool-pwsh/tests/tools.spec.ts index 67ac413252..c50af1a02f 100644 --- a/packages/shell/tool-pwsh/tests/tools.spec.ts +++ b/packages/shell/tool-pwsh/tests/tools.spec.ts @@ -247,9 +247,11 @@ function sandboxAgent( session: { id, header: { version: 0, id, createdAt: 0 }, + get seq() { return events.length }, + eventAt: (seq: number) => events[seq], snapshotEvents: () => events, append: (type: string, data: Record) => { - const event = { type, data } + const event = { type, data, seq: events.length } events.push(event) onAppend?.(type) return event @@ -270,7 +272,13 @@ function registerFakeAgent(ctx: Context, sessionId: string): Agent { const agent = { id, ctx: scopeFiber.ctx, - session: { id, header: { version: 0, id, createdAt: 0 }, snapshotEvents: () => [] }, + session: { + id, + header: { version: 0, id, createdAt: 0 }, + seq: 0, + eventAt: () => undefined, + snapshotEvents: () => [], + }, } as unknown as Agent ctx.agents.register(agent) return agent @@ -602,9 +610,10 @@ describe('sandbox escalation through ctx.approval', () => { expect(prompted).not.toHaveBeenCalled() const malformed = sandboxAgent() - ;(malformed.session.snapshotEvents() as unknown as Array<{ type: string; data: { mode: string } }>).push({ + ;(malformed.session.snapshotEvents() as unknown as Array<{ type: string; data: { mode: string }; seq: number }>).push({ type: 'sandbox/mode', data: { mode: 'unknown-mode' }, + seq: malformed.session.seq, }) expect(text(await call(ctx, 'pwsh', escalate, malformed))).toContain('not strictly wider') }) diff --git a/packages/skill/tool-skill/tests/tool-skill.spec.ts b/packages/skill/tool-skill/tests/tool-skill.spec.ts index b665d2e1bb..7a488ba2e0 100644 --- a/packages/skill/tool-skill/tests/tool-skill.spec.ts +++ b/packages/skill/tool-skill/tests/tool-skill.spec.ts @@ -585,6 +585,18 @@ describe('dsh-tool-skill', () => { expect(JSON.stringify(published[0]?.data.content)).toContain('live-skill') }) + it('rejects a missing event below the current Session length', async () => { + const home = await tempDir('tool-catalog-missing-event') + const ctx = await setup(home) + const session = Session.create(SessionId('catalog-missing-event')) + const agent = sessionAgent(session) + openMessageTurn(session) + Object.defineProperty(session, 'eventAt', { value: () => undefined }) + + await expect(fireStep(ctx, agent, 1, 1)) + .rejects.toThrow('skill catalog cannot read seq 1 below the current Session length') + }) + it('re-establishes the current catalog after compaction hides its durable message', async () => { const home = await tempDir('tool-catalog-compaction') const ctx = await setup(home)