test(compaction): stabilize overflow snapshots and cover entry points

This commit is contained in:
Shuhan Lin committed 2026-10-23 09:53:08 +08:00
1 parent cf7229e72a
commit cd6187f901
16 files changed
+192 -13

No files matched your search

@@ -27,8 +27,8 @@
en: 3446c54c062d7e34
zh: fb502c941e508ae0
/deepseek-ai-dsh-compaction-basic/use-this-package/what-happens-when-condensation-runs:
en: 399bce86e63b8e19
zh: 727c023210085f5c
en: 7284ba61343adcdd
zh: efd0113ab8e8e5ce
/deepseek-ai-dsh-compaction-basic/use-this-package/switching-models:
en: 48978abc46da1afa
zh: 11931af05db7b41f
@@ -51,8 +51,8 @@
en: 967531b71453eb88
zh: c7919b46e36763e6
/deepseek-ai-dsh-compaction-basic/understand-the-implementation/the-region-transaction:
en: 0492e288c55028cb
zh: 88a85c06c0cc78d2
en: febc0a9de3eabe5f
zh: a570cc5d171c31b7
/deepseek-ai-dsh-compaction-basic/understand-the-implementation/config-resolution:
en: 313e019b1a9f8aa8
zh: 3c75d11b02adb693
@@ -83,7 +83,7 @@ Misconfiguration fails fast: unknown settings, duplicate per-model overrides, in
The oldest balanced span is replaced by one summary message and the recent tail stays verbatim; the conversation continues from the summary. The operation reports how many history items were condensed and the estimated tokens freed. If nothing can be condensed safely — for example the whole conversation is one indivisible unit — nothing changes and nothing is written to the session log. If no model is available to write the summary (no configured target and no routed request yet), condensation fails with a clear error telling you to configure the summarization provider and model or route one request.
If an automatically selected or `/compact` summary receives `CONTEXT_WINDOW_EXCEEDED`, condensation retries the first half of the selected complete history units, keeping each tool call with its results. Each further overflow halves the remaining units; a single unit that still overflows fails. A successful checkpoint replaces only the prefix actually summarized, leaving the remaining messages and images intact. Explicit `compactRegion(start, end)` calls keep their exact selection and propagate summary overflow.
If an automatically selected or `/compact` summary receives `CONTEXT_WINDOW_EXCEEDED`, condensation retries the first half of the selected complete history units, rounded up to a whole unit, keeping each tool call with its results. Each further overflow applies the same reduction; a single unit that still overflows fails. A successful checkpoint replaces only the prefix actually summarized, leaving the remaining messages and images intact.
### Switching models
@@ -134,6 +134,8 @@ A direct `ctx.llm.stream()` call uses the configured provider/model pair and cap
Failed summary requests dispatch synchronous `compaction/summary-error` after checking cancellation and selection stability. A recovery listener must record a durable input change before requesting retry. The backend re-derives the selected messages and refreshes their token prices and shrink baseline. The image-offload plugin owns image selection; its recorded omissions remain effective if the summary later fails or is cancelled. When no listener handles a canonical context overflow, self-selected ranges may shrink within the same transaction; only the successful prefix appears in the checkpoint's cited range.
Explicit `compactRegion(start, end)` calls keep their exact selection and propagate summary overflow.
The transaction validates the surface span and the durable lock, appends `compaction/start`, summarizes through the hook, revalidates stability (whole-surface for automatic calls, selected-span for manual calls), rejects a summary that does not shrink its source, appends `compaction/summary` plus the replacement `user/message`, and makes exactly one `compaction/end` attempt. A live unmatched start is the durable lock: an unmatched marker before a newer `session/end-seed` is stale evidence from a prior lifecycle and does not block; one after that boundary reports `busy`. A failed close deliberately leaves a blocking orphan. Cancellation remains authoritative after cleanup and durability.
### Config resolution
@@ -83,7 +83,7 @@ kind: "package-reference"
最旧的平衡范围会被替换为一条摘要消息,近期尾部保持逐字不变;对话从摘要继续。操作会报告压缩了多少历史项以及估算释放的 token 数。如果没有任何内容可以安全压缩——例如整个对话就是一个不可分单元——则不会有任何改变,也不会向会话日志写入任何内容。如果没有模型可以撰写摘要(既未配置目标,也还没有已路由请求),压缩会失败并给出清晰错误,提示你配置摘要提供方与模型,或先路由一次请求。
自动选区或 `/compact` 的摘要收到 `CONTEXT_WINDOW_EXCEEDED` 时,压缩会重试所选完整历史单元的前一半,每个工具调用及其结果保持在一起。每次再次超限都会将剩余单元折半;单个单元仍超限时失败。成功检查点只替换实际完成摘要的前缀,其余消息和图片保持不变。显式 `compactRegion(start, end)` 调用保持精确选区,并向调用方报告摘要超限。
自动选区或 `/compact` 的摘要收到 `CONTEXT_WINDOW_EXCEEDED` 时,压缩会重试所选完整历史单元的前一半(单元数向上取整),每个工具调用及其结果保持在一起。每次再次超限都按同一规则缩小范围;单个单元仍超限时失败。成功检查点只替换实际完成摘要的前缀,其余消息和图片保持不变。
### 切换模型
@@ -134,6 +134,8 @@ kind: "package-reference"
摘要请求失败后,后端先检查取消和选区稳定性,再触发同步的 `compaction/summary-error`。恢复监听器必须先记录持久的输入变更,才能请求重试。后端重新派生所选消息,更新 token 价格及判断摘要是否缩短输入的基准。图片省略插件负责选图;后续摘要失败或取消后,已经记录的省略仍然有效。没有监听器处理规范上下文超限时,自选范围可以在同一事务中缩小;检查点只引用成功摘要的前缀范围。
显式 `compactRegion(start, end)` 调用保持精确选区,并向调用方报告摘要超限。
事务验证表层范围与持久锁,追加 `compaction/start`,通过钩子生成摘要,重新验证稳定性(自动调用要求整个表层、手动调用只要求所选范围),拒绝不缩小源内容的摘要,追加 `compaction/summary` 与替换 `user/message`,并恰好进行一次 `compaction/end` 尝试。活动的未匹配 start 是持久锁:位于较新 `session/end-seed` 之前的未匹配标记是先前生命周期留下的陈旧证据,不会阻塞;位于该边界之后的标记报告 `busy`。闭合失败会有意留下阻塞性的未匹配标记。完成清理与持久化后,取消仍具有最终决定权。
### 配置解析
@@ -13,7 +13,7 @@ import {
resolveTargetPolicy,
} from '@deepseek-ai/dsh-compaction-basic/src/config.ts'
import type { CompactionResult } from '@deepseek-ai/dsh-compaction'
import LlmRuntime, { createUserMessage, ToolCallId, CONTEXT_WINDOW_EXCEEDED_CODE, createSystemMessage, createToolResultMessage, LlmAdapter , createMessage } from '@deepseek-ai/dsh-llm'
import LlmRuntime, { createUserMessage, ToolCallId, CONTEXT_WINDOW_EXCEEDED_CODE, createSystemMessage, createToolResultMessage, LlmAdapter, LlmError, createMessage } from '@deepseek-ai/dsh-llm'
import type { ContextFormed } from '@deepseek-ai/dsh-llm'
import type {
ContentBlock,
@@ -865,6 +865,58 @@ describe('pressure measurement and retention', () => {
expect(session.snapshotEvents().at(-1)).toMatchObject({ type: 'compaction/end', data: { error: 'summary failed' } })
})
it('shares one pressure-and-retention measurement before opening the compaction transaction', async () => {
const ctx = createContext()
try {
const compact = service(compactConfig, ctx)
const session = conversation(4)
const measuredRevisions: number[] = []
const originalMeasure = ctx.tokenMeter.measure.bind(ctx.tokenMeter)
const measure = vi.spyOn(ctx.tokenMeter, 'measure').mockImplementation((measuredSession, header) => {
measuredRevisions.push(measuredSession.seq)
return originalMeasure(measuredSession, header)
})
const failure = new Error('summary failed')
compact.error = failure
await expect(compactIfNeeded(compact, session, 'pressure')).rejects.toBe(failure)
const start = session.snapshotEvents().find(event => event.type === 'compaction/start')
if (start === undefined) throw new Error('pressure must open a compaction transaction')
expect(measuredRevisions.filter(revision => revision <= start.seq)).toHaveLength(1)
expect(measure.mock.calls[0]).toEqual([session, session.requestHeader()])
expect(measuredRevisions.some(revision => revision > start.seq)).toBe(true)
} finally {
await ctx.fiber.dispose()
}
})
it('recovers a pressure summary overflow below threshold while retaining the excluded suffix', async () => {
const ctx = createContext()
try {
const compact = service({ auto: false, thresholdRatio: 0.65, retainTokens: 180, compactionRetries: 0 }, ctx)
const session = conversation(4)
const originalNodes = [...session.surface.nodes]
const originalMessages = session.deriveMessages()
expect(ctx.tokenMeter.measure(session).totalTokens).toBeGreaterThanOrEqual(650)
compact.mutateDuringSummary = () => {
if (compact.calls.length === 1) throw new LlmError('summary input exceeds the model context', CONTEXT_WINDOW_EXCEEDED_CODE)
}
const result = await compactIfNeeded(compact, session, 'pressure')
expect(compact.calls.map(call => call.input.messages.length)).toEqual([6, 3])
expect(result?.shadowedSeqs).toEqual(originalNodes.slice(0, 3))
expect(session.surface.nodes.slice(1)).toEqual(originalNodes.slice(3))
expect(session.deriveMessages().slice(1)).toEqual(originalMessages.slice(3))
expect(ctx.tokenMeter.measure(session).totalTokens).toBeLessThan(650)
expect(session.snapshotEvents().filter(event => event.type.startsWith('compaction/')).map(event => event.type))
.toEqual(['compaction/start', 'compaction/summary', 'compaction/end'])
} finally {
await ctx.fiber.dispose()
}
})
it('bounds retries when a shrinking checkpoint remains above threshold', async () => {
const compact = service({
auto: false,
@@ -8,9 +8,11 @@ import { BasicCompactionEngine } from '@deepseek-ai/dsh-compaction-basic'
import { CompactionId, isCompactCheckpointSource, ManualCompactionError } from '@deepseek-ai/dsh-compaction'
import type { CompactionResult } from '@deepseek-ai/dsh-compaction'
import {
CONTEXT_WINDOW_EXCEEDED_CODE,
createAssistantMessage,
createUserMessage,
LlmAdapter,
LlmError,
} from '@deepseek-ai/dsh-llm'
import type {
ContentBlock,
@@ -239,6 +241,54 @@ function compactEvents(session: Session): SessionEvent[] {
}
describe('compactNow through the real loop', () => {
it('recovers an idle summary overflow and flushes before releasing a queued prompt', async () => {
const harness = await loopHarness()
const { ctx, agent, compact, adapter, log } = harness
const retryEntered = deferred()
const gate = deferred()
let running: Promise<CompactionResult | null> | undefined
try {
for (let turn = 0; turn < 3; turn += 1) await seedHistory(harness)
const originalNodes = [...agent.session.surface.nodes]
const originalMessages = agent.session.deriveMessages()
const flush = vi.spyOn(ctx.sessions, 'flush')
compact.duringSummary = () => {
if (compact.calls.length === 1) throw new LlmError('summary input exceeds the model context', CONTEXT_WINDOW_EXCEEDED_CODE)
compact.gate = gate.promise
retryEntered.resolve()
}
running = compact.compactNow(agent, SIGNAL)
await Promise.race([retryEntered.promise, running])
expect(compact.calls.map(input => input.messages.length)).toEqual([6, 4])
expect(log).toEqual(['compaction/start:null'])
agent.followup(createUserMessage({
content: [{ type: 'text', text: 'Continue after the smaller checkpoint.' }], source: { kind: 'user' },
}))
expect(adapter.requests).toHaveLength(3)
gate.resolve()
const result = await running
expect(result?.shadowedSeqs).toEqual(originalNodes.slice(1, 4))
expect(flush).toHaveBeenCalledWith(agent.session)
await agent.whenIdle()
const events = compactEvents(agent.session)
expect(events.map(event => event.type)).toEqual(['compaction/start', 'compaction/summary', 'compaction/end'])
expect(events[0]?.data).toMatchObject({ turn: null })
expect(events[2]?.data).toMatchObject({ turn: null })
expect(log.indexOf('compaction/end:null')).toBeLessThan(log.indexOf('flush'))
expect(log.indexOf('flush')).toBeLessThan(log.indexOf('turn/start'))
expect(adapter.requests).toHaveLength(4)
expect(adapter.requests[3]?.slice(2, -1)).toEqual(originalMessages.slice(4))
expect(adapter.requests[3]?.at(-1)?.content).toEqual([{ type: 'text', text: 'Continue after the smaller checkpoint.' }])
} finally {
gate.resolve()
if (running !== undefined) await Promise.allSettled([running])
await ctx.fiber.dispose()
}
})
it('passes logged image omissions to the summarizer without changing the original message', async () => {
const { ctx, agent, compact } = await loopHarness()
try {
@@ -27,8 +27,8 @@
en: 6b11d5111e1a3222
zh: de4d3889b1a5ebb4
/deepseek-ai-dsh-session-snapshot/use-this-package/platform-and-composition-variants:
en: 64666ea2894acc4a
zh: 0e78df32bcfd3633
en: 33c70ff7900f61c7
zh: b7032a64bf1de40a
/deepseek-ai-dsh-session-snapshot/use-this-package/what-can-go-wrong:
en: 0b03592947ca43d6
zh: b14a64238c4b5f80
@@ -90,6 +90,8 @@ A pin owns its generated `system-prompt.expected.md` or `tool-schemas.expected.j
A scenario requiring a non-Windows host declares `posixOnly`, which skips its run test on Windows while the fixture guards keep covering its committed files everywhere; a scenario whose composition needs a usable `pwsh` declares `pwshOnly`. `workspaceParent` moves the generated child cwd outside the platform temp directory when temporary-directory grants are themselves under test; a scenario's committed `workspace/` is copied into that child first, then `prepareWorkspace` runs against the generated cwd before the agent starts. Default generated workspaces are stored in session fixtures as `{{cwd}}`, so platform temp roots and random basenames do not affect recordings. Headless manifests use `workspace.parent: outside-temp` when the Session workspace grant itself is under test. The adapter allocates beside the platform temp root when its parent is writable and outside system temporary grants, otherwise under home, and rejects any generated cwd already covered by automatic temporary write grants.
ACP scenarios that persist token prices for runtime-context text can set `workspaceCwdTextLength: 192`. The harness pads the canonical temporary cwd to that JSON-quoted length and retains a unique random suffix; it rejects lengths that cannot accommodate the temporary root before allocating directories. Other scenarios keep their ordinary temporary paths.
### What can go wrong
- **A durable wait times out** — `waitForTurnStart`, `waitForTurnEnd`, `waitForSubagentTurnEnd`, `waitForGoalPhase`, `waitForInboxMessage`, `waitForTitleAfterTurnEnd`, and `waitForEventAfterTurnEnd` identify the session or child and deadline even when the first log harvest exceeds that deadline. Child waits also name the requested turn. All retain the underlying failure as the error cause. `waitForTurnStart` rejects malformed turn records immediately.
@@ -90,6 +90,8 @@ Headless/ACP 与 SDK 适配器在规范化之前,将原始目录中的子创
需要非 Windows 主机的场景声明 `posixOnly`,在 Windows 上跳过运行测试,但 fixture 保护仍在所有平台覆盖其已提交文件;组合需要可用 `pwsh` 的场景声明 `pwshOnly`。当临时目录授权自身待测时,`workspaceParent` 将生成子级 cwd 移出平台临时区域;场景签入的 `workspace/` 会先复制到该子级,随后 `prepareWorkspace` 在 agent 启动前针对生成 cwd 运行。默认生成的 workspace 在会话 fixture 中存储为 `{{cwd}}`,使平台临时根目录与随机 basename 不影响录制。headless manifest 在测试 Session workspace 授权本身时使用 `workspace.parent: outside-temp`。适配器在父目录可写且位于系统临时授权之外时,于平台临时根目录旁分配目录,否则使用 home,并拒绝已被自动临时写授权覆盖的生成 cwd。
持久记录 runtime-context 文本 token 计价的 ACP 场景可设置 `workspaceCwdTextLength: 192`。harness 将规范临时 cwd 填充至该 JSON 引用字符串长度,并保留唯一随机后缀;若指定长度容不下临时根目录,则在分配目录前拒绝。其他场景保留普通临时路径。
### 可能出什么问题
- **持久记录等待超时**——即使首次日志收集就超过期限,`waitForTurnStart`、`waitForTurnEnd`、`waitForSubagentTurnEnd`、`waitForGoalPhase`、`waitForInboxMessage`、`waitForTitleAfterTurnEnd` 和 `waitForEventAfterTurnEnd` 也会指出会话或子会话及等待期限。子会话等待还会指出目标轮次。这些等待都通过错误的 cause 保留底层失败。`waitForTurnStart` 会立即拒绝格式错误的轮次记录。
@@ -211,6 +211,12 @@ export interface RunOptions {
* child, never the supplied parent.
*/
workspaceParent?: string
/**
* Exact JSON-quoted cwd length for fixtures that persist path-dependent token prices.
* Pads a canonical temporary path while retaining mkdtemp's random suffix;
* an insufficient or non-integer length rejects before directory allocation.
*/
workspaceCwdTextLength?: number
/**
* Alternate live profile patch (absolute), overriding
* {@link AgentUnderTest.configPath} for this run. Its basename still ends
@@ -236,6 +242,20 @@ export function snapshotSpillRoot(
return `${root}/dsh-acp-snap-${key}`
}
/** Keep path-priced fixtures independent of the temporary root without sharing directories. */
function workspaceTemporaryPrefix(options: RunOptions): string {
const parent = options.workspaceParent ?? tmpdir()
const length = options.workspaceCwdTextLength
if (length === undefined) return join(parent, 'acp-snap-cwd-')
const prefix = join(realpathSync(parent), 'acp-snap-cwd-')
// mkdtemp contributes six characters; runtime-context renders the JSON-quoted cwd.
const minimum = JSON.stringify(`${prefix}XXXXXX`).length
if (!Number.isSafeInteger(length) || length < minimum) {
throw new Error(`snapshot-harness: workspaceCwdTextLength must be an integer of at least ${minimum}`)
}
return prefix + '-'.repeat(length - minimum)
}
/**
* Run a scenario end-to-end against a freshly-spawned subprocess. Owns the
* child and its generated dirs; always tears them down. Returns the captured stdout
@@ -246,7 +266,7 @@ export function snapshotSpillRoot(
* @returns The captured stdout/stderr, session id, generated cwd, and harvested logs.
*/
export async function runScenario(input: InputScript, opts: RunOptions): Promise<RunResult> {
const cwd = await mkdtemp(join(opts.workspaceParent ?? tmpdir(), 'acp-snap-cwd-'))
const cwd = await mkdtemp(workspaceTemporaryPrefix(opts))
const cwdAliases = [...new Set([realpathSync(cwd), realpathSync.native(cwd)])]
const sessionsRoot = await mkdtemp(join(tmpdir(), 'acp-snap-sessions-'))
let spillRoot: string | undefined
@@ -183,6 +183,8 @@ export interface Scenario {
* test and the scenario needs an independent project location.
*/
workspaceParent?: string
/** Exact JSON-quoted cwd length forwarded to the harness for path-priced fixtures. */
workspaceCwdTextLength?: number
/**
* Optional final workspace preparation after the committed fixture is
* copied. Reserve this for paths that Git cannot represent portably; normal
@@ -1337,6 +1339,7 @@ export function defineAcpSnapshotSuite(options: SnapshotSuiteOptions): void {
...existsSync(workspaceDir) ? { workspaceDir } : {},
...scenario.prepareWorkspace !== undefined ? { prepareWorkspace: scenario.prepareWorkspace } : {},
...scenario.workspaceParent !== undefined ? { workspaceParent: scenario.workspaceParent } : {},
...scenario.workspaceCwdTextLength === undefined ? {} : { workspaceCwdTextLength: scenario.workspaceCwdTextLength },
// A scenario passes its live profile patch; the launcher derives
// the sibling `*cordis.snapshot.yml` for replay.
...scenario.configPath !== undefined ? { configPath: scenario.configPath } : {},
@@ -683,6 +683,49 @@ describe('runScenario', () => {
expect(child.startsWith(`..${sep}`)).toBe(false)
})
it('fixes the serialized cwd length across different temporary parents and removes each generated directory', async () => {
const { fixtureFile } = await scenario({})
const parent = await mkdtemp(join(tmpdir(), 'acp-snap-path-length-'))
tempDirs.push(parent)
const longerParent = join(parent, 'a-longer-parent-directory')
await mkdir(longerParent)
const settled = await Promise.allSettled([parent, longerParent].map(workspaceParent => runScenario(
{ steps: boot },
{
agent: AGENT, mode: 'replay', fixtureFile, workspaceParent, workspaceCwdTextLength: 192,
prepareWorkspace: async (cwd) => {
expect(cwd).toBe(await realpath(cwd))
expect(JSON.stringify(cwd).length).toBe(192)
},
},
)))
const results = settled.map((result) => {
if (result.status === 'rejected') throw result.reason
return result.value
})
expect(new Set(results.map(result => result.cwd)).size).toBe(2)
for (const result of results) {
expect(JSON.stringify(result.cwd).length).toBe(192)
expect(result.cwd).toMatch(/acp-snap-cwd-[-a-zA-Z0-9]+$/)
await expect(readdir(result.cwd)).rejects.toMatchObject({ code: 'ENOENT' })
}
expect(await readdir(parent)).toEqual(['a-longer-parent-directory'])
})
it.each([1, 192.5])('rejects unusable serialized cwd length %s before allocating a workspace', async (workspaceCwdTextLength) => {
const { fixtureFile } = await scenario({})
const allocate = vi.spyOn(fsPromises, 'mkdtemp')
try {
await expect(runScenario(
{ steps: boot },
{ agent: AGENT, mode: 'replay', fixtureFile, workspaceCwdTextLength },
)).rejects.toThrow('workspaceCwdTextLength')
expect(allocate).not.toHaveBeenCalled()
} finally {
allocate.mockRestore()
}
})
it('promptAndCancel waits for the durable turn start, cancels, and settles the prompt', { timeout: 20_000 }, async () => {
const { fixtureFile } = await scenario({ prompt: 'hang-until-cancel' })
const result = await runScenario(
@@ -106,9 +106,11 @@ const REPLAY_SCENARIOS: Scenario[] = [
env: { DSH_PERMISSION_MODE: 'never' },
configPath: AGENT.configPath,
workspaceParent: tmpdir(),
workspaceCwdTextLength: 192,
pinsChildToolSchemas: [1],
pinsChildSystemPrompts: [1],
prepareWorkspace: (cwd) => {
expect(JSON.stringify(cwd).length).toBe(192)
writeFileSync(join(cwd, 'seed.txt'), 'prepared at runtime')
},
},
+2
View File
@@ -27,6 +27,7 @@ const controllerCases: readonly {
readonly name: string
readonly hasModelTurn: boolean
readonly configPath?: string
readonly workspaceCwdTextLength?: number
}[] = [
{ name: 'handshake', hasModelTurn: false },
{ name: 'reject-extra-dirs', hasModelTurn: false },
@@ -45,6 +46,7 @@ const controllerCases: readonly {
name: 'summary-overflow',
hasModelTurn: true,
configPath: join(corpusDir, 'summary-overflow', 'cordis.yml'),
workspaceCwdTextLength: 192,
},
] as const
@@ -30,7 +30,7 @@
{"type":"user/message","data":{"content":[{"type":"text","text":"Now reply with exactly the single word DONE."}],"source":{"kind":"user"},"role":"user","id":"{{message:7}}"},"surfaceOp":"append"}
{"type":"assistant/attempt","data":{"turn":3,"step":1,"stream":[{"type":"chunk","time":1792679887096,"chunk":{"type":"finish","reason":{"kind":"error","failure":{"code":"CONTEXT_WINDOW_EXCEEDED","message":"screenshot history exceeds the authored context window"}}}}]}}
{"type":"compaction/start","data":{"compactionId":"{{id:1}}","turn":3}}
{"type":"compaction/summary","data":{"compactionId":"{{id:1}}","summary":[{"type":"text","text":"The first group of three dashboard screenshots was inspected and acknowledged."}],"rawOutput":[{"type":"text","text":"The first group of three dashboard screenshots was inspected and acknowledged."}],"llmStreamCall":true,"shadowedRange":{"start":8,"end":13},"shadowedSeqs":[8,9,13],"shadowedTokenCount":319,"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp","maxTokens":8192,"usage":{"inputTokens":20,"outputTokens":16}}}
{"type":"compaction/summary","data":{"compactionId":"{{id:1}}","summary":[{"type":"text","text":"The first group of three dashboard screenshots was inspected and acknowledged."}],"rawOutput":[{"type":"text","text":"The first group of three dashboard screenshots was inspected and acknowledged."}],"llmStreamCall":true,"shadowedRange":{"start":8,"end":13},"shadowedSeqs":[8,9,13],"shadowedTokenCount":349,"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp","maxTokens":8192,"usage":{"inputTokens":20,"outputTokens":16}}}
{"type":"user/message","data":{"content":[{"type":"text","text":"This is an automatically generated checkpoint condensing an earlier span of the conversation to free up context. Treat the captured context as established background and build on it without restating it. Continue the task directly from the messages that follow, without acknowledging this checkpoint.\n\n<compacted-summary>"},{"type":"text","text":"The first group of three dashboard screenshots was inspected and acknowledged."},{"type":"text","text":"</compacted-summary>"}],"source":{"kind":"compact-checkpoint","compactionId":"{{id:1}}"},"role":"user","id":"{{message:8}}"},"sourceEventSeqs":[30,31,8,9,13],"surfaceOp":{"op":"replace","startSeq":8,"endSeq":13}}
{"type":"compaction/end","data":{"compactionId":"{{id:1}}","turn":3}}
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent working directory: \"{{cwd}}\".\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"working-directory:current","text":"Current working directory: \"{{cwd}}\"."},{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}]},"role":"user","id":"{{message:9}}"},"surfaceOp":"append"}
+1 -1
View File
@@ -6,7 +6,7 @@ recording: authored
header:
class: summary-overflow
pin: true
systemPromptSource: session/read-image
systemPromptSource: image-compaction
toolSchemasSource: escalation-approved
permission: danger-full-access
input:
@@ -1 +0,0 @@
../../session/read-image/system-prompt.expected.md