|
|
@@ -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)
|