fix(schedule): validate time context snapshot markers

This commit is contained in:
pku-xht
2026-08-07 16:58:15 +08:00
committed by Tianyi Cui
parent 01ed2c9e1c
commit d090f57bdb
4 changed files with 29 additions and 9 deletions

View File

@@ -6,6 +6,7 @@
import type { Context } from 'cordis'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import { deriveClientTimeZoneContext } from '@deepseek-ai/dsh-time-context'
import { defineTool } from '@deepseek-ai/dsh-tools'
import type { GenericCallView } from '@deepseek-ai/dsh-tools'
@@ -217,6 +218,22 @@ interface AtTimeZoneContext {
readonly clientTimeZones: string[]
}
/** Whether one durable message is the exact time-context snapshot marker. */
function isTimeContextReading(event: SessionEvent): boolean {
if (event.type !== 'user/message') return false
const source = event.data.source
const [block] = event.data.content
return event.data.content.length === 1
&& block?.type === 'text'
&& source.kind === 'plugin'
&& source.plugin === 'time-context'
&& Object.keys(source).length === 4
&& source.form === 'snapshot'
&& source.sections.length === 1
&& source.sections[0]?.name === 'time-context'
&& source.sections[0].text === block.text
}
/** Derive request zones only while the current open turn contains a time-context reading. */
function currentClientTimeZoneContext(agent: Agent): ReturnType<typeof deriveClientTimeZoneContext> | undefined {
const events = agent.session.events
@@ -236,10 +253,7 @@ function currentClientTimeZoneContext(agent: Agent): ReturnType<typeof deriveCli
if (stepStart < 0) return undefined
const turnStart = events.findLastIndex(event => event.type === 'turn/start' && event.data.turn === turn)
if (turnStart < 0) return undefined
const hasReading = events.slice(turnStart + 1).some(event => event.type === 'user/message'
&& event.data.source.kind === 'plugin'
&& event.data.source.plugin === 'time-context'
&& Object.keys(event.data.source).length === 2)
const hasReading = events.slice(turnStart + 1).some(isTimeContextReading)
if (!hasReading) return undefined
const messages = events.slice(turnStart + 1)
.flatMap(event => event.type === 'user/message' ? [event.data] : [])

View File

@@ -98,9 +98,15 @@ function appendRequestContext(agent: Agent, clientTimeZones: readonly string[]):
source: { kind: 'user', clientTimeZone } as never,
}), { surfaceOp: 'append' })
}
const text = 'time context'
agent.session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'time context' }],
source: { kind: 'plugin', plugin: 'time-context' },
content: [{ type: 'text', text }],
source: {
kind: 'plugin',
plugin: 'time-context',
form: 'snapshot',
sections: [{ name: 'time-context', text }],
},
}), { surfaceOp: 'append' })
}
@@ -329,7 +335,7 @@ describe('Schedule tool protocol', () => {
})
})
it('reuses a simple same-turn marker across an empty continuation and ignores a malformed source', async () => {
it('reuses a same-turn snapshot marker across an empty continuation and ignores a malformed source', async () => {
const test = await harness(true, 'Asia/Shanghai')
test.agent.session.append('turn/start', { turn: 1 })
test.agent.session.append('step/start', { turn: 1, step: 1 })