request-log.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. /**
  2. * Per-loop-instance request-header bookkeeping for reconstructability. The
  3. * comparison baseline is folded from the session log; a fresh instance anchors
  4. * it with an initial/resume snapshot and later logs full changed snapshots.
  5. *
  6. * @module dsh-agent-loop/request-log
  7. */
  8. import { headerEquals } from '@deepseek-ai/dsh-session'
  9. import type { EpochHeader, Session } from '@deepseek-ai/dsh-session'
  10. import type { Message } from '@deepseek-ai/dsh-llm'
  11. /** Per-loop-instance bookkeeping: whether THIS instance has logged a header yet. */
  12. export interface TransmissionLog {
  13. /** True once this loop instance appended its anchoring `request/header` snapshot. */
  14. loggedHeader: boolean
  15. /**
  16. * The instance's composed session prefix (the `agent/session-prefix`
  17. * waterfall's deep-frozen product), cached on the instance's first
  18. * request-building step and reused verbatim for every request it sends —
  19. * the structural guarantee that the prefix never changes mid-session.
  20. * `undefined` until composed.
  21. */
  22. sessionPrefix?: Message[]
  23. }
  24. /**
  25. * Fresh bookkeeping for a newly-started loop instance.
  26. * @returns state with `loggedHeader` false, so the instance's first request appends an anchoring snapshot.
  27. */
  28. export function createTransmissionLog(): TransmissionLog {
  29. return { loggedHeader: false }
  30. }
  31. /**
  32. * Append the full header snapshot owed by this request: initial/resume for the
  33. * instance's first request, nothing when unchanged, or change otherwise.
  34. *
  35. * @param session - the session whose log explains the request.
  36. * @param state - this loop instance's bookkeeping (mutated on first log).
  37. * @param header - the canonical header the request will ACTUALLY use
  38. * (post-`agent/request`).
  39. */
  40. export function recordRequestHeader(session: Session, state: TransmissionLog, header: EpochHeader): void {
  41. if (!state.loggedHeader) {
  42. session.append('request/header', { header, reason: session.requestHeader() === undefined ? 'initial' : 'resume' })
  43. state.loggedHeader = true
  44. return
  45. }
  46. // This instance logged a snapshot, so the fold is necessarily defined.
  47. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  48. const baseline = session.requestHeader()!
  49. if (headerEquals(baseline, header)) return
  50. session.append('request/header', { header, reason: 'change' })
  51. }