mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Review discussion converged on the industry shape (Claude Code caches user context per conversation; Codex separates initial context from diffs; Kimi appends at continuation boundaries to protect prompt caching): stable openers belong in a compose-once prefix, mid-session changes belong in append-only history — not in a per-request slot. agent/session-prefix fires ONCE per loop instance, lazily on its first request-building step: the composed Message[] is deep-frozen, cached on the transmission bookkeeping, recorded as EpochHeader.messagePrefix on the anchoring 'initial'/'resume' snapshot, and reused verbatim for every request the instance sends — prefix stability is structural, not a producer discipline, and a resume recomposes with attributable drift. The request is messagePrefix + boundary snapshot. The per-step RequestAdvice/RequestAdviceContext surface and the messageSuffix header field are dropped: the tail slot had no consumer, and every current update pattern (new AGENTS.md discovered, memory update, skills change) routes through the existing append-only history channels — inject(), tools/post-execute additionalContext, prompt-submit additionalContext — each paid once and prefix-cached thereafter. The messagePrefix delta arm stays for codec totality; the loop never produces one in practice.
81 lines
3.7 KiB
TypeScript
81 lines
3.7 KiB
TypeScript
/**
|
|
* Per-loop-instance transmission bookkeeping for the reconstructability
|
|
* contract: which header event to append before a request so the session log
|
|
* always explains the request (the reconstructability RFC). The loop is
|
|
* otherwise transmission-stateless — the comparison baseline is the log's own
|
|
* folded header (`Session.requestHeader()`), so resume and fork need no
|
|
* special path: a fresh loop instance simply logs a `'resume'` snapshot on
|
|
* its first request and deltas from there.
|
|
*
|
|
* @module dsh-agent-loop/request-log
|
|
*/
|
|
|
|
import { diffHeader, headerEquals, applyHeaderDelta } from '@deepseek-ai/dsh-session'
|
|
import type { EpochHeader, Session } from '@deepseek-ai/dsh-session'
|
|
import type { Message } from '@deepseek-ai/dsh-llm'
|
|
|
|
/** Per-loop-instance bookkeeping: whether THIS instance has logged a header yet. */
|
|
export interface TransmissionLog {
|
|
/** True once this loop instance appended its anchoring `request/header` snapshot. */
|
|
loggedHeader: boolean
|
|
/**
|
|
* The instance's composed session prefix (the `agent/session-prefix`
|
|
* waterfall's deep-frozen product), cached on the instance's first
|
|
* request-building step and reused verbatim for every request it sends —
|
|
* the structural guarantee that the prefix never changes mid-session.
|
|
* `undefined` until composed.
|
|
*/
|
|
sessionPrefix?: Message[]
|
|
}
|
|
|
|
/**
|
|
* Fresh bookkeeping for a newly-started loop instance.
|
|
* @returns state with `loggedHeader` false, so the instance's first request appends an anchoring snapshot.
|
|
*/
|
|
export function createTransmissionLog(): TransmissionLog {
|
|
return { loggedHeader: false }
|
|
}
|
|
|
|
/**
|
|
* Append whatever header event this request owes the log, so folding the log
|
|
* reproduces the header the request was built under. Exactly one of four
|
|
* things happens:
|
|
*
|
|
* 1. This loop instance has not logged a header yet → a full `request/header`
|
|
* snapshot anchors the fold: reason `'initial'` when the log has no header
|
|
* events at all (a new conversation), `'resume'` when it does (process
|
|
* restart, fork seed — the boundary itself is a recorded fact, so the
|
|
* snapshot is appended even when nothing changed).
|
|
* 2. The header equals the folded baseline → nothing; the log already
|
|
* explains this request.
|
|
* 3. It differs and the delta round-trips (`applyHeaderDelta` on the baseline
|
|
* reproduces the header exactly) → a `request/header-delta`.
|
|
* 4. It differs and the delta encoding cannot express the change (a pure tool
|
|
* reordering) → a full snapshot with reason `'fallback'`; deltas are an
|
|
* encoding optimization, never a correctness dependency.
|
|
*
|
|
* @param session - the session whose log explains the request.
|
|
* @param state - this loop instance's bookkeeping (mutated on first log).
|
|
* @param header - the canonical header the request will ACTUALLY use
|
|
* (post-`agent/request`).
|
|
*/
|
|
export function recordRequestHeader(session: Session, state: TransmissionLog, header: EpochHeader): void {
|
|
if (!state.loggedHeader) {
|
|
session.append('request/header', { header, reason: session.requestHeader() === undefined ? 'initial' : 'resume' })
|
|
state.loggedHeader = true
|
|
return
|
|
}
|
|
// This instance logged a snapshot, so the fold is necessarily defined.
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
const baseline = session.requestHeader()!
|
|
if (headerEquals(baseline, header)) return
|
|
const delta = diffHeader(baseline, header)
|
|
/* v8 ignore next -- headerEquals false ⟹ diffHeader defined: both compare the same four parts */
|
|
if (delta === undefined) return
|
|
if (headerEquals(applyHeaderDelta(baseline, delta), header)) {
|
|
session.append('request/header-delta', delta)
|
|
} else {
|
|
session.append('request/header', { header, reason: 'fallback' })
|
|
}
|
|
}
|