fix(e2e): decode packed history records

This commit is contained in:
kingwl 2026-08-24 11:28:57 +08:00 • committed by imccyu
parent 04c0758fe9
commit 4f02717ebc
5 changed files with 25 additions and 19 deletions

View file

@ -11,6 +11,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { setTimeout as delay } from 'node:timers/promises'
import { fileURLToPath } from 'node:url'
import { decodeStorageRecord } from '@deepseek-ai/dsh-session/chunk-rows'
import { describe, expect, it } from 'vitest'
import WebSocket from 'ws'
@ -59,15 +60,15 @@ interface WorkspaceBaseline {
}
interface HistoryPage {
events: Array<{
event: {
type: string
data: unknown
}
}>
records: Array<{ event: HistoryEvent } | { chunks: unknown }>
hasMore: boolean
}
interface HistoryEvent {
type: string
data: unknown
}
interface ProcessObservation {
readonly ready: Promise<string>
readonly text: () => string
@ -255,10 +256,10 @@ async function history(baseUrl: string, sessionId: string): Promise<HistoryPage>
{ request: { address: { kind: 'session', sessionId }, maxMessages: 100 } },
value => isRecord(value)
&& value.type === 'snapshot'
&& Array.isArray(value.events)
&& Array.isArray(value.records)
&& typeof value.hasMore === 'boolean',
)
return { events: frame.events as HistoryPage['events'], hasMore: frame.hasMore as boolean }
return { records: frame.records as HistoryPage['records'], hasMore: frame.hasMore as boolean }
}
/** Poll a public observation until it satisfies the test's behavior predicate. */
@ -294,7 +295,7 @@ async function eventually<T>(
/** Return every text block from durable assistant messages. */
function assistantText(page: HistoryPage): string {
const text: string[] = []
for (const { event } of page.events) {
for (const event of historyEvents(page)) {
if (event.type !== 'assistant/message' || !isRecord(event.data) || !isRecord(event.data.message)) continue
const content = event.data.message.content
if (!Array.isArray(content)) continue
@ -305,6 +306,11 @@ function assistantText(page: HistoryPage): string {
return text.join('\n')
}
/** Expand lossless history records for assertions over the public event stream. */
function historyEvents(page: HistoryPage): HistoryEvent[] {
return page.records.flatMap(record => 'event' in record ? [record.event] : decodeStorageRecord(record.chunks))
}
/** Stop the spawned CLI through its normal signal path, escalating only on a stuck teardown. */
async function stop(child: ChildProcess): Promise<void> {
if (child.exitCode !== null) return
@ -410,7 +416,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('GitHub webhook through the real
'webhook provenance, title, and permission events',
async () => await history(baseUrl, sessionId),
(page) => {
const events = page.events.map(item => item.event)
const events = historyEvents(page)
const title = events.find(event => event.type === 'session/title')
const permission = events.find(event =>
event.type === 'permission/preset'
@ -429,7 +435,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('GitHub webhook through the real
},
30_000,
)
const webhookMessage = admitted.events.map(item => item.event)
const webhookMessage = historyEvents(admitted)
.find(event => event.type === 'user/message'
&& isRecord(event.data)
&& isRecord(event.data.source)

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/event-producer-consumer.md
event-producer-consumer.md: 6f971f5470c34093dd3774650ad191f9a3516df7
event-producer-consumer.md: ce914f90821adc09c59dca15f202831a51655565
event-producer-consumer.zh.md: ca375c98b6c0552f255e5d2604c9ad0c1d968aad

View file

@ -21,11 +21,11 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `agent/session-start` | `emit` | [`packages/core/agent/src/runtime-types.ts:224`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | `agent-team`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:185`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`schedule`](../packages/schedule/schedule), `server`, `session-controller` |
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:285`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:482`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:462`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:489`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/removed` | `emit` | [`packages/api/session-controller/src/types.ts:468`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/status` | `emit` | [`packages/api/session-controller/src/types.ts:475`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:491`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:471`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:498`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/removed` | `emit` | [`packages/api/session-controller/src/types.ts:477`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/status` | `emit` | [`packages/api/session-controller/src/types.ts:484`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/types.ts:85`](../packages/interaction/user-approval/src/types.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `remotes` |
| `authorization/settled` | `emit` | [`packages/credentials/authorization/src/index.ts:57`](../packages/credentials/authorization/src/index.ts) | [`authorization`](../packages/credentials/authorization) (`events.dispatch`) | [`authorization`](../packages/credentials/authorization) |
| `commands/change` | `emit` | [`packages/interaction/commands/src/types.ts:80`](../packages/interaction/commands/src/types.ts) | [`commands`](../packages/interaction/commands) (`events.dispatch`) | `remotes` |

View file

@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/persistence-catalog.md
persistence-catalog.md: 12558eeadc009b498c9a178cfcc79116bf1b7c2b
persistence-catalog.md: 7c63cd8dfdb181fac09cf3ce2e4bcec96d2bd59c
persistence-catalog.zh.md: f855d6969aa2dcade159ac8d6549e5f0350a7f0f

View file

@ -512,7 +512,7 @@ Source: [`packages/llm/llm-retry/src/types.ts:11`](../packages/llm/llm-retry/src
'model/selection': ModelSelection
```
Source: [`packages/api/session-controller/src/types.ts:39`](../packages/api/session-controller/src/types.ts)
Source: [`packages/api/session-controller/src/types.ts:40`](../packages/api/session-controller/src/types.ts)
### `permission/*`