Преглед на файлове

chore(session): permit existing deprecated history reads

_Kerman преди 1 месец
родител
ревизия
37abcb74c5
променени са 100 файла, в които са добавени 580 реда и са изтрити 11 реда
  1. 2 2
      .agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml
  2. 3 1
      .agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md
  3. 3 1
      .agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md
  4. 1 0
      apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts
  5. 1 0
      apps/cli/tests/profiles/headless/tests/compaction.e2e.ts
  6. 1 0
      apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts
  7. 3 0
      apps/cli/tests/profiles/headless/tests/ptc.e2e.ts
  8. 1 0
      apps/cli/tests/profiles/headless/tests/resume.e2e.ts
  9. 1 0
      apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts
  10. 1 0
      apps/web/tests/chat-continuous-conversation.e2e.ts
  11. 11 0
      apps/web/tests/chat-long-interactions.e2e.ts
  12. 1 0
      apps/web/tests/chat-scroll-fixture.ts
  13. 1 0
      apps/web/tests/clickable-links-gallery.e2e.ts
  14. 1 0
      apps/web/tests/complex-history.perf.ts
  15. 1 0
      apps/web/tests/feedback-release.e2e.ts
  16. 1 0
      apps/web/tests/goal-command-presentation.e2e.ts
  17. 1 0
      apps/web/tests/markdown-cjk-strong.e2e.ts
  18. 1 0
      apps/web/tests/markdown-images.e2e.ts
  19. 1 0
      apps/web/tests/markdown-inline-code-links.e2e.ts
  20. 1 0
      apps/web/tests/markdown-wide-table.e2e.ts
  21. 1 0
      apps/web/tests/math-rendering.e2e.ts
  22. 1 0
      apps/web/tests/minimal-preset.snapshot.ts
  23. 1 0
      apps/web/tests/preset-migration.snapshot.ts
  24. 1 0
      apps/web/tests/produced-file-mentions.e2e.ts
  25. 1 0
      apps/web/tests/produced-files.e2e.ts
  26. 2 0
      apps/web/tests/reference-composer.e2e.ts
  27. 2 0
      apps/web/tests/scaffold.ts
  28. 8 0
      apps/web/tests/schedule-after.e2e.ts
  29. 1 0
      apps/web/tests/seeded-history.e2e.ts
  30. 4 0
      apps/web/tests/settings-chrome.e2e.ts
  31. 1 0
      apps/web/tests/subagent-interrupt-ui.e2e.ts
  32. 2 0
      apps/web/tests/subagent-interrupt.e2e.ts
  33. 2 2
      docs/config-catalog.i18n.yaml
  34. 1 1
      docs/config-catalog.md
  35. 1 1
      docs/config-catalog.zh.md
  36. 2 0
      packages/acp/acp/tests/bridge.spec.ts
  37. 5 0
      packages/acp/acp/tests/turns.spec.ts
  38. 2 0
      packages/api/session-controller/src/commands.ts
  39. 1 0
      packages/api/session-controller/src/history.ts
  40. 1 0
      packages/api/session-controller/src/index.ts
  41. 1 0
      packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts
  42. 3 0
      packages/api/session-controller/tests/session-cold.host.spec.ts
  43. 6 0
      packages/api/session-controller/tests/session-fork.host.spec.ts
  44. 3 0
      packages/api/session-controller/tests/session-projections.host.spec.ts
  45. 1 0
      packages/api/session-controller/tests/session-rename.host.spec.ts
  46. 1 0
      packages/api/session-controller/tests/transport.host.spec.ts
  47. 1 0
      packages/bundle/headless/src/index.ts
  48. 1 0
      packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts
  49. 1 0
      packages/compaction/command-compact/tests/command-compact.spec.ts
  50. 1 0
      packages/compaction/command-compact/tests/loader-composition.spec.ts
  51. 6 3
      packages/compaction/compaction-basic/src/region.ts
  52. 29 0
      packages/compaction/compaction-basic/tests/compaction-basic.spec.ts
  53. 12 0
      packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts
  54. 29 0
      packages/compaction/compaction-basic/tests/manual-compaction.spec.ts
  55. 1 0
      packages/compaction/compaction-tool-result-pruner/src/index.ts
  56. 4 0
      packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts
  57. 1 0
      packages/compaction/compaction/src/invariant.ts
  58. 1 0
      packages/compaction/compaction/src/tool-pairing.ts
  59. 3 0
      packages/compaction/compaction/tests/compaction.spec.ts
  60. 8 0
      packages/compaction/compaction/tests/invariant.spec.ts
  61. 1 0
      packages/compaction/compaction/tests/tool-pairing.spec.ts
  62. 2 0
      packages/context/agent-instructions/src/index.ts
  63. 1 0
      packages/context/agent-instructions/src/state.ts
  64. 3 0
      packages/context/agent-instructions/tests/agent-instructions.e2e.ts
  65. 34 0
      packages/context/agent-instructions/tests/agent-instructions.spec.ts
  66. 1 0
      packages/context/session-reference/tests/loader-composition.spec.ts
  67. 4 0
      packages/context/session-reference/tests/session-reference.spec.ts
  68. 1 0
      packages/context/time-context/src/index.ts
  69. 2 0
      packages/context/time-context/src/invariant.ts
  70. 13 0
      packages/context/time-context/tests/time-context.spec.ts
  71. 2 0
      packages/context/tmux-context/tests/tmux-context.spec.ts
  72. 1 0
      packages/core/agent-loop/src/index.ts
  73. 1 0
      packages/core/agent-loop/src/invariant.ts
  74. 2 0
      packages/core/agent-loop/src/runtime-context.ts
  75. 1 0
      packages/core/agent-loop/tests/agent-initiator.spec.ts
  76. 4 0
      packages/core/agent-loop/tests/agent.spec.ts
  77. 42 0
      packages/core/agent-loop/tests/cancel.spec.ts
  78. 40 0
      packages/core/agent-loop/tests/contract-regressions.spec.ts
  79. 18 0
      packages/core/agent-loop/tests/coverage-edges.spec.ts
  80. 4 0
      packages/core/agent-loop/tests/inbox.spec.ts
  81. 1 0
      packages/core/agent-loop/tests/interception.spec.ts
  82. 69 0
      packages/core/agent-loop/tests/loop.spec.ts
  83. 4 0
      packages/core/agent-loop/tests/properties.spec.ts
  84. 1 0
      packages/core/agent-loop/tests/request-cache.e2e.ts
  85. 6 0
      packages/core/agent-loop/tests/request-error.spec.ts
  86. 3 0
      packages/core/agent-loop/tests/request-freeze.spec.ts
  87. 41 0
      packages/core/agent-loop/tests/request-reconstruction.spec.ts
  88. 9 0
      packages/core/agent-loop/tests/resume.spec.ts
  89. 3 0
      packages/core/agent-loop/tests/scope-lifecycle.spec.ts
  90. 20 0
      packages/core/agent-loop/tests/system-prompt-admission.spec.ts
  91. 1 0
      packages/core/agent-loop/tests/tool-calls.spec.ts
  92. 8 0
      packages/core/agent-loop/tests/tool-order.spec.ts
  93. 13 0
      packages/core/agent/tests/consumed-work.spec.ts
  94. 4 0
      packages/core/session/src/index.ts
  95. 1 0
      packages/core/session/src/invariant.ts
  96. 5 0
      packages/core/session/tests/canonical-envelopes.spec.ts
  97. 1 0
      packages/core/session/tests/derived-cache.spec.ts
  98. 13 0
      packages/core/session/tests/fork.spec.ts
  99. 2 0
      packages/core/session/tests/invariant.spec.ts
  100. 8 0
      packages/core/session/tests/properties.spec.ts

+ 2 - 2
.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md
-2026-09-09-deprecate-synchronous-session-event-reads.md: b0bc1e81ada1781587b7bd6b5687d62bc0d8e798
-2026-09-09-deprecate-synchronous-session-event-reads.zh.md: 0c8fd24bb48e6a40309fe571fce5d2fba5b99b2b
+2026-09-09-deprecate-synchronous-session-event-reads.md: 0fcbfbc2e21792f67d1bdbe6d32ac3106338ac45
+2026-09-09-deprecate-synchronous-session-event-reads.zh.md: d1f22b483380a7a38a5cd1208815c5fc0ae25348

+ 3 - 1
.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md

@@ -34,11 +34,13 @@ Fork and a small number of operations may genuinely need a complete historical s
 
 **Require every existing caller to migrate immediately.** Existing logic may defer migration under this decision. Preventing new dependencies bounds the remaining work without making every existing consumer part of the same change.
 
+**Disable deprecation lint for these methods or whole files.** Such exemptions also admit new calls. Line-scoped waivers make the existing migration debt explicit while retaining the native type-aware rule for new reads.
+
 ## Consequences
 
 New domain behavior must make its event data and projected state sufficient for resumed execution. User-requested history may still load progressively, and genuine full-history operations still have a storage path to design. This decision does not claim that resume or fork already avoids loading the complete log.
 
-The current `typescript/no-deprecated` lint rule reports both existing and new calls. The JSDoc records the migration deferral, but lint does not yet distinguish permitted existing callers from prohibited additions. The documentation checks verify the source-equivalent API declarations and bilingual records; they do not enforce that distinction.
+Existing calls carry line-scoped `typescript/no-deprecated` waivers with a migration-deferral reason. The rule stays enabled for new calls and unrelated deprecated APIs. Remove a waiver when its call migrates; copying a waiver to a new call violates this policy. The executable lint check exercises all three readers with and without waivers, plus an unrelated deprecated API. Documentation checks verify the source-equivalent API declarations and bilingual records.
 
 ## Related decisions
 

+ 3 - 1
.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md

@@ -34,11 +34,13 @@ fork 等少数操作可能确实需要完整历史序列或继承前缀。它们
 
 **要求立即迁移所有现有调用方。** 本决策允许现有逻辑推迟迁移。禁止新增依赖可以限制剩余工作规模,无需将每个现有消费方都纳入同一次修改。
 
+**为这些方法或整个文件禁用弃用 lint。** 这种豁免也会放过新增调用。逐行豁免明确标出已有的迁移负担,同时继续由原生类型感知规则检查新增读取。
+
 ## 后果
 
 新增领域行为必须使其事件数据与投影状态足以支持恢复后的执行。用户请求的历史仍可渐进式加载,确实需要完整历史的操作仍需设计存储读取路径。本决策不表示恢复或 fork 已经能够避免加载完整日志。
 
-当前 `typescript/no-deprecated` lint 规则会同时报告已有调用与新增调用。JSDoc 记录了暂缓迁移的约定,但 lint 尚不能区分允许保留的现有调用与禁止新增的调用。文档检查校验与源码一致的 API 声明及双语记录,并不强制执行这种区分。
+现有调用带有逐行的 `typescript/no-deprecated` 豁免,并说明暂缓迁移的理由。该规则对新增调用及其他已弃用 API 继续生效。调用迁移后应删除豁免;将豁免复制到新增调用违反本策略。实际执行 lint 的检查覆盖三个读取方法有无豁免的情况,以及另一个已弃用 API。文档检查校验与源码一致的 API 声明及双语记录。
 
 ## 相关决策
 

+ 1 - 0
apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts

@@ -67,6 +67,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('coding task: fix a failing test
     await waitForIdle(ctx, agent)
 
     // The agent claims success…
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const summary = finalText(agent.session.snapshotEvents()).toLowerCase()
     expect(summary.length).toBeGreaterThan(0)
 

+ 1 - 0
apps/cli/tests/profiles/headless/tests/compaction.e2e.ts

@@ -56,6 +56,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('compaction: a long session compa
       }], source: { kind: 'user' } }))
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents()
 
     // A compaction ran: the start…end bracket landed in the real log.

+ 1 - 0
apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts

@@ -34,6 +34,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('full loop: real model + real bas
     agent.followup(createUserMessage({ content: [{ type: 'text', text: 'Run `echo e2e-ok` with the bash tool and tell me its exact output.' }], source: { kind: 'user' } }))
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents()
     const calls = events.filter(event => event.type === 'tool/call')
     expect(calls.length).toBeGreaterThan(0)

+ 3 - 0
apps/cli/tests/profiles/headless/tests/ptc.e2e.ts

@@ -366,6 +366,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('PTC mode: real model writes a pr
         + 'and return only the joined string.',
       }], source: { kind: 'user' } }))
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events: readonly SessionEvent[] = agent.session.snapshotEvents()
 
     // The wire contract: every request this session made offered EXACTLY ONE
@@ -418,10 +419,12 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('PTC mode: real model writes a pr
       }], source: { kind: 'user' } }))
     await waitForIdle(ctx, handle.agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events: readonly SessionEvent[] = handle.agent.session.snapshotEvents()
     const dispatch = events.find(event => event.type === 'tool/ptc-dispatch' && event.data.name === 'read')
     const outerResult = events.find(event => event.type === 'tool/result')
     const workspaceContext = await vi.waitFor(() => {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const splice = handle.agent.session.snapshotEvents().findLast(event => event.type === 'agent/inbox/spliced'
         && event.data.inserted.some(message => message.source.kind === 'agent-instructions'))
       const inserted = splice?.type === 'agent/inbox/spliced'

+ 1 - 0
apps/cli/tests/profiles/headless/tests/resume.e2e.ts

@@ -63,6 +63,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('resume: continue a persisted ses
     await waitForIdle(ctx, resumed)
 
     // The model recalls it — only possible from the resumed history.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(finalText(resumed.session.snapshotEvents())).toContain(SECRET)
   }, 180_000)
 })

+ 1 - 0
apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts

@@ -37,6 +37,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('todo_write: real model records a
       + 'Send all three in one todo_write call, then reply with the single word DONE.' }], source: { kind: 'user' } }))
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents()
 
     // The model actually called the tool.

+ 1 - 0
apps/web/tests/chat-continuous-conversation.e2e.ts

@@ -334,6 +334,7 @@ describe('web e2e: continuous conversation grown through the composer', () => {
     }
 
     if (sessionId === undefined) throw new Error('continuous conversation completed no turn')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(scaffold.ctx.agents.get(sessionId)?.session.snapshotEvents().filter(event => (
       event.type === 'turn/end' && event.data.reason.kind === 'completed'
     ))).toHaveLength(TURN_COUNT)

+ 11 - 0
apps/web/tests/chat-long-interactions.e2e.ts

@@ -184,12 +184,17 @@ describe('web e2e: long Chat interaction contract', () => {
     const toolAssistantMarker = FIXTURE.markers.assistant(TOOL_TURN)
     const toolMarker1 = FIXTURE.markers.tool(TOOL_TURN, 1)
     const toolMarker2 = FIXTURE.markers.tool(TOOL_TURN, 2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const toolUserEvent = requiredEvent(source.session.snapshotEvents(), 'user/message', toolUserMarker)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const toolAssistantEvent = requiredEvent(source.session.snapshotEvents(), 'assistant/message', toolAssistantMarker)
     const branchUserMarker = FIXTURE.markers.user(BRANCH_TURN)
     const branchAssistantMarker = FIXTURE.markers.assistant(BRANCH_TURN)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const branchUserEvent = requiredEvent(source.session.snapshotEvents(), 'user/message', branchUserMarker)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const branchAssistantEvent = requiredEvent(source.session.snapshotEvents(), 'assistant/message', branchAssistantMarker)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const boundary = source.session.snapshotEvents().find((event): event is SessionEvent<'turn/end'> => (
       event.type === 'turn/end' && event.data.turn === BRANCH_TURN
     ))
@@ -315,8 +320,11 @@ describe('web e2e: long Chat interaction contract', () => {
       .find(agent => agent.session.header.parentSession === SessionId(SESSION_ID))
     if (child === undefined) throw new Error('message branch did not create a child session')
     expect(child.session.inheritedEventCount).toBe(boundary.seq + 1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child.session.snapshotEvents().some(event => carries(event, branchAssistantMarker))).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child.session.snapshotEvents().some(event => carries(event, FIXTURE.markers.user(BRANCH_TURN + 1)))).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child.session.snapshotEvents().some(event => carries(event, FIXTURE.markers.user(FIXTURE.turns)))).toBe(false)
 
     const currentCrumb = page.getByRole('navigation', { name: 'Session hierarchy' })
@@ -334,10 +342,13 @@ describe('web e2e: long Chat interaction contract', () => {
     await expect.poll(() => page.locator('[data-streaming="true"]').count(), { timeout: 15_000 }).toBe(0)
     expect(await composer.textContent()).toBe('')
     expect(await composer.isEnabled()).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(source.session.snapshotEvents().some(event => carries(event, CONTINUE_PROMPT))).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child.session.snapshotEvents().filter(event => (
       event.type === 'user/message' && carries(event, CONTINUE_PROMPT)
     ))).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const lastTurnEnd = child.session.snapshotEvents().findLast((event): event is SessionEvent<'turn/end'> => (
       event.type === 'turn/end'
     ))

+ 1 - 0
apps/web/tests/chat-scroll-fixture.ts

@@ -178,6 +178,7 @@ function fixtureLog(session: Session): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify(event)),
     '',
   ].join('\n')

+ 1 - 0
apps/web/tests/clickable-links-gallery.e2e.ts

@@ -287,6 +287,7 @@ function galleryFixture(imageUrl: string): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event,
       time: eventTimeOrigin + event.seq * 1_000,

+ 1 - 0
apps/web/tests/complex-history.perf.ts

@@ -329,6 +329,7 @@ function fixtureLog(session: Session): string {
   }
   return [
     JSON.stringify(header),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify(event)),
     '',
   ].join('\n')

+ 1 - 0
apps/web/tests/feedback-release.e2e.ts

@@ -275,6 +275,7 @@ describe.each(MODE === 'record' ? ['deepseek-official'] : ['deepseek-official',
     expect(captured()).toHaveLength(releasedCount)
     await input.press('Enter')
     const findHeaderless = () => scaffold.ctx.sessions.list().find(session => session.id !== sessionId
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       && session.snapshotEvents().some(event => event.type === 'feedback/record'))
     // A command-only session keeps the hero view; its durable event confirms submission.
     await expect.poll(findHeaderless, { timeout: 10_000 }).toBeDefined()

+ 1 - 0
apps/web/tests/goal-command-presentation.e2e.ts

@@ -124,6 +124,7 @@ describe('web e2e: /goal human transcript presentation', () => {
 
     const sessions = scaffold.ctx.sessions.list()
     expect(sessions).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const persisted = sessions[0]?.snapshotEvents() ?? []
     expect(persisted.filter(event => event.type === 'command/run' || event.type === 'command/done')
       .map(event => event.type)).toEqual(['command/run', 'command/done'])

+ 1 - 0
apps/web/tests/markdown-cjk-strong.e2e.ts

@@ -79,6 +79,7 @@ function markdownFixture(): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event,
       time: eventTimeOrigin + event.seq * 1_000,

+ 1 - 0
apps/web/tests/markdown-images.e2e.ts

@@ -144,6 +144,7 @@ function markdownImageFixture(remoteUrl: string, outsidePath: string): string {
     // the stats line renders its LLM segment only while the step's measured
     // milliseconds exceed zero, so a fixture that leaves the times unset lets
     // the replay's own speed decide whether the golden matches.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event,
       time: eventTimeOrigin + event.seq * 1_000,

+ 1 - 0
apps/web/tests/markdown-inline-code-links.e2e.ts

@@ -76,6 +76,7 @@ function markdownFixture(linkUrl: string): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event,
       time: eventTimeOrigin + event.seq * 1_000,

+ 1 - 0
apps/web/tests/markdown-wide-table.e2e.ts

@@ -142,6 +142,7 @@ function wideTableFixture(): string {
   return [
     JSON.stringify(header),
     // Spaced event times, as the sibling markdown fixtures pin them.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event,
       time: eventTimeOrigin + event.seq * 1_000,

+ 1 - 0
apps/web/tests/math-rendering.e2e.ts

@@ -80,6 +80,7 @@ function mathFixture(): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event,
       time: eventTimeOrigin + event.seq * 1_000,

+ 1 - 0
apps/web/tests/minimal-preset.snapshot.ts

@@ -81,6 +81,7 @@ describe('minimal agent preset', () => {
     if (requestHeader === undefined) throw new Error('the minimal agent issued no model request')
     const systemPrompt = systemPromptText(agentHandle.agent.session)
     if (systemPrompt === undefined) throw new Error('the minimal agent issued no system prompt')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agentHandle.agent.session.snapshotEvents().some(event => event.type === 'user/message'
       && event.data.source.kind === 'plugin'
       && event.data.source.plugin === '@deepseek-ai/dsh-system-prompt')).toBe(false)

+ 1 - 0
apps/web/tests/preset-migration.snapshot.ts

@@ -45,6 +45,7 @@ describe.skipIf(webSnapshotMode() === 'record')('historical preset restoration t
       if ('error' in resolved) throw resolved.error
       expect(scaffold.ctx.agentPresets.composedPreset(resolved.agent.ctx)).toBe('ptc')
       expect(resolved.agent.session.header.agentPreset).toBe('ptc')
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(resolved.agent.session.snapshotEvents()
         .filter(event => event.type === 'agent-preset/selected')
         .map(event => event.data.agentPreset)).toEqual(withSelections ? ['ptc', 'standard', 'ptc'] : [])

+ 1 - 0
apps/web/tests/produced-file-mentions.e2e.ts

@@ -112,6 +112,7 @@ function mentionFixture(): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event,
       time: eventTimeOrigin + event.seq * 1_000,

+ 1 - 0
apps/web/tests/produced-files.e2e.ts

@@ -99,6 +99,7 @@ function producedFixture(): string {
       type: 'session', version: SESSION_FORMAT_VERSION, id: '{{sessionId}}',
       createdAt: 0, cwd: '{{cwd}}', isSeeded: false, delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify({
       ...event, time: eventTimeOrigin + event.seq * 1_000,
     })),

+ 2 - 0
apps/web/tests/reference-composer.e2e.ts

@@ -73,6 +73,7 @@ function sourceSessionFixture(): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify(event)),
     '',
   ].join('\n')
@@ -122,6 +123,7 @@ function targetSessionFixture(): string {
       isSeeded: false,
       delegationDepth: 0,
     }),
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ...session.snapshotEvents().map(event => JSON.stringify(event)),
     '',
   ].join('\n')

+ 2 - 0
apps/web/tests/scaffold.ts

@@ -877,6 +877,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
  * in-memory record-mode harvest, so the on-disk zstd default never matters.
  */
 function rawSessionLog(session: Session): string {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   const encodedEvents = (session.snapshotEvents() as unknown as readonly SessionFormatEvent[])
     .map(event => sessionFormatCatalog.encodeCurrentEvent(event))
   const header = sessionFormatCatalog.encodeCurrentHeader({
@@ -1030,6 +1031,7 @@ async function assertReplaySession(
   const userPrompts = fixtureUserPrompts(expected)
   const candidates = sessions.filter((session) => {
     if (session.header.parentSession !== undefined) return false
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const actual = session.snapshotEvents().flatMap((event) => {
       if (event.type !== 'user/message' || event.data.source.kind !== 'user') return []
       const text = event.data.content.filter(block => block.type === 'text').map(block => block.text).join('')

+ 8 - 0
apps/web/tests/schedule-after.e2e.ts

@@ -205,6 +205,7 @@ async function waitForReply(
 ): Promise<SessionEvent<'assistant/message'>> {
   const deadline = Date.now() + timeoutMs
   while (true) {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = handle.agent.session.snapshotEvents().find((candidate): candidate is SessionEvent<'assistant/message'> => (
       candidate.type === 'assistant/message' && assistantText(candidate) === text
     ))
@@ -455,6 +456,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
   it('batches one latest occurrence per overdue Every record into an ordinary follow-up', async () => {
     onTestFailed(() => saveFailureShot(page, 'web-e2e-schedule-every'))
     const ids = new Set(everyRecords.map(record => record.id))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const dispatches = everyHandle.agent.session.snapshotEvents().filter(event => (
       event.type === 'schedule/change'
       && event.data.operation === 'dispatch'
@@ -470,6 +472,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
     const decision = acceptedAt[0]
     if (decision === undefined) throw new Error('missing Every decision time')
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const batch = everyHandle.agent.session.snapshotEvents().find(event => (
       event.type === 'user/message'
       && event.data.source.kind === 'plugin'
@@ -493,6 +496,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
     if (reminderRequest === undefined) throw new Error('model did not receive the Every batch')
     expect(requestText(reminderRequest)).toContain(batchBlock.text)
     expectReminderFraming(reminderRequest)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const active = foldScheduleEvents(everyHandle.agent.session.snapshotEvents()).active
     expect(active).toHaveLength(2)
     expect(active.every(record => Date.parse(record.scheduledAt) > Date.parse(decision))).toBe(true)
@@ -516,6 +520,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
 
   it('uses request-local browser context to create an explicit local At reminder', async () => {
     onTestFailed(() => saveFailureShot(page, 'web-e2e-schedule-at'))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const user = atHandle.agent.session.snapshotEvents().find(event => (
       event.type === 'user/message'
       && event.data.source.kind === 'user'
@@ -541,11 +546,13 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
     }
     expect(selectedAt.time_zone).toBe(AT_BROWSER_ZONE)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const toolCall = atHandle.agent.session.snapshotEvents().find(event => (
       event.type === 'tool/call' && event.data.name === 'schedule_create'
     ))
     if (toolCall?.type !== 'tool/call') throw new Error('missing schedule_create tool call')
     expect(JSON.parse(toolCall.data.arguments)).toEqual({ prompt: AT_PROMPT, at: selectedAt })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const created = atHandle.agent.session.snapshotEvents().find(event => (
       event.type === 'schedule/change'
       && event.data.operation === 'create'
@@ -560,6 +567,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
       prompt: AT_PROMPT,
       scheduledAt,
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(atHandle.agent.session.snapshotEvents().filter(event => (
       event.type === 'schedule/change'
       && event.data.operation === 'dispatch'

+ 1 - 0
apps/web/tests/seeded-history.e2e.ts

@@ -495,6 +495,7 @@ describe('web e2e: seeded history renders through cold resume', () => {
 
       const agent = scaffold.ctx.agents.get(SessionId(SEED_ID))
       if (agent === undefined) throw new Error('seeded session did not attach an agent')
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const done = agent.session.snapshotEvents().filter(event => event.type === 'command/done').at(-1)
       if (done?.type !== 'command/done') throw new Error('feedback command did not settle')
       const [sessionLine, userLine, extraLine] = done.data.text?.split('\n') ?? []

+ 4 - 0
apps/web/tests/settings-chrome.e2e.ts

@@ -144,6 +144,7 @@ describe('web e2e: settings modal and General preferences', () => {
   it('stores Permission as the default for future sessions without changing an existing session', async () => {
     onTestFailed(() => saveFailureShot(page, 'web-e2e-settings-permission'))
     const existing = scaffold.ctx.sessions.create(SessionId('settings-permission-before'))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(existing.snapshotEvents().find(event => event.type === 'permission/preset')?.data)
       .toEqual({ preset: 'workspace-write' })
 
@@ -160,10 +161,12 @@ describe('web e2e: settings modal and General preferences', () => {
     const document = await readFile(join(scaffold.harnessHome, 'settings.yaml'), 'utf8')
     expect(document).toContain('permission:')
     expect(document).toContain('defaultPreset: read-only')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(existing.snapshotEvents().find(event => event.type === 'permission/preset')?.data)
       .toEqual({ preset: 'workspace-write' })
 
     const created = scaffold.ctx.sessions.create(SessionId('settings-permission-after'))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(created.snapshotEvents().map(event => [event.type, event.data])).toEqual([
       ['permission/preset', { preset: 'read-only' }],
       ['sandbox/mode', { mode: 'read-only' }],
@@ -181,6 +184,7 @@ describe('web e2e: settings modal and General preferences', () => {
     const confirmedDocument = await readFile(join(scaffold.harnessHome, 'settings.yaml'), 'utf8')
     expect(confirmedDocument).toContain('defaultPreset: danger-full-access')
     const confirmed = scaffold.ctx.sessions.create(SessionId('settings-permission-confirmed'))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(confirmed.snapshotEvents().map(event => [event.type, event.data])).toEqual([
       ['permission/preset', { preset: 'danger-full-access' }],
       ['sandbox/mode', { mode: 'danger-full-access' }],

+ 1 - 0
apps/web/tests/subagent-interrupt-ui.e2e.ts

@@ -320,6 +320,7 @@ describe.skipIf(MODE === 'record')('web e2e: composer interrupt for a running co
     const child = scaffold.ctx.agents.get(childId)
     expect(child).toBeDefined()
     expect(child!.inbox.nextTurn).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child!.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(2)
     await page.getByRole('button', { name: 'Send message' }).waitFor({ timeout: 15_000 })
 

+ 2 - 0
apps/web/tests/subagent-interrupt.e2e.ts

@@ -159,7 +159,9 @@ describe.skipIf(MODE === 'record')('web e2e: subagents/interruptByParent over th
     expect(child).toBeDefined()
     expect(child!.status).toBe('idle')
     expect(child!.inbox.nextTurn).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child!.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const lastEnd = child!.session.snapshotEvents().filter(event => event.type === 'turn/end').at(-1)
     expect((lastEnd)?.data.reason.kind).toBe('aborted')
 

+ 2 - 2
docs/config-catalog.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/config-catalog.md
-config-catalog.md: 50626a7d85aa54f6d2e3cdb6140b3af8081d1461
-config-catalog.zh.md: d2ae35b91a4a83da882e8ec5af82a505aa5d5bd1
+config-catalog.md: 0b56a574cac7f42efafaac8fb80047027441e7dc
+config-catalog.zh.md: ac10d61b7c2f14513819caef2faba992ca84451e

+ 1 - 1
docs/config-catalog.md

@@ -3236,7 +3236,7 @@ export interface Config {
 export type ApprovalPolicy = 'ask' | 'never'
 ```
 
-Source: [`packages/interaction/user-approval/src/index.ts:127`](../packages/interaction/user-approval/src/index.ts)
+Source: [`packages/interaction/user-approval/src/index.ts:128`](../packages/interaction/user-approval/src/index.ts)
 
 <a id="deepseek-aidsh-web"></a>
 

+ 1 - 1
docs/config-catalog.zh.md

@@ -3238,7 +3238,7 @@ export interface Config {
 export type ApprovalPolicy = 'ask' | 'never'
 ```
 
-来源:[`packages/interaction/user-approval/src/index.ts:126`](../packages/interaction/user-approval/src/index.ts)
+来源:[`packages/interaction/user-approval/src/index.ts:128`](../packages/interaction/user-approval/src/index.ts)
 
 <a id="deepseek-aidsh-web"></a>
 

+ 2 - 0
packages/acp/acp/tests/bridge.spec.ts

@@ -902,6 +902,7 @@ describe('automation-only ACP bridge', () => {
     expect(secondImage.attachment.mediaType).toBe('image/jpeg')
     expect(secondImage.attachment.bytes).toBe(1)
     const agent = harness.ctx.agents.get(SessionId(sessionId))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(JSON.stringify(agent?.session.snapshotEvents())).not.toContain('AQ==')
   })
 
@@ -975,6 +976,7 @@ describe('automation-only ACP bridge', () => {
       sessionId,
       prompt: [{ type: 'image', data: '', mimeType: 'image/png' }],
     })).rejects.toThrow(/inline image prompts were not advertised/)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(harness.ctx.agents.get(SessionId(sessionId))?.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(false)
   })
 

+ 5 - 0
packages/acp/acp/tests/turns.spec.ts

@@ -216,6 +216,7 @@ describe('ACP prompt lifecycle', () => {
     const prompt = harness.client.prompt({ sessionId, prompt: [{ type: 'text', text: 'go' }] })
       .finally(() => { settled = true })
     await vi.waitFor(() => {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().filter(event => event.type === 'agent/inbox/spliced'
         && event.data.inserted.length > 0)).toHaveLength(2)
     })
@@ -316,6 +317,7 @@ describe('ACP prompt lifecycle', () => {
 
     await expect(first).resolves.toEqual({ stopReason: 'cancelled' })
     expect(harness.adapter.requests).toEqual([])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = harness.ctx.agents.get(SessionId(sessionId))?.session.snapshotEvents() ?? []
     expect(events.some(event => event.type === 'user/message' || event.type === 'turn/start')).toBe(false)
   })
@@ -443,6 +445,7 @@ describe('ACP prompt lifecycle', () => {
     await harness.client.cancel({ sessionId })
     await expect(prompt).resolves.toEqual({ stopReason: 'cancelled' })
     await agent.whenIdle()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')?.data.reason)
       .toEqual({ kind: 'aborted', reason: { kind: 'user' } })
   })
@@ -468,12 +471,14 @@ describe('ACP prompt lifecycle', () => {
       source: { kind: 'plugin', plugin: 'test' },
     }))
     await vi.waitFor(() => {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true)
     })
 
     await harness.client.cancel({ sessionId })
     await agent.whenIdle()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')?.data.reason)
       .toEqual({ kind: 'aborted', reason: { kind: 'user' } })
   })

+ 2 - 0
packages/api/session-controller/src/commands.ts

@@ -541,6 +541,7 @@ export class SessionCommandController {
   private async readSessionState(sessionId: SessionId): Promise<SessionReadState> {
     const attached = this.ctx.sessions.get(sessionId)
     if (attached !== undefined) {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       return { id: attached.id, header: attached.header, events: attached.snapshotEvents() }
     }
     const inspected = await inspectApiSession(this.ctx, sessionId)
@@ -587,6 +588,7 @@ function hasPromptRequest(agent: Agent, requestId: SessionRequestId): boolean {
     return source.kind === 'user' && 'rpcId' in source && source.rpcId === requestId
   }
   if (agent.inbox.nextTurn.some(matches) || agent.inbox.nextStep.some(matches)) return true
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents().some((event) => {
     if (event.type !== 'user/message') return false
     const source = event.data.source

+ 1 - 0
packages/api/session-controller/src/history.ts

@@ -152,6 +152,7 @@ export class SessionHistoryController {
       // Constructor seed events have no session/event notification. Normally
       // only the end-seed suffix is new; if persistence advanced after the
       // opening observation, replay everything beyond that snapshot cursor.
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const suffix = session.snapshotEvents(snapshotCursor === undefined
         ? session.firstLiveSeq
         : SessionLogOffset(snapshotCursor + 1))

+ 1 - 0
packages/api/session-controller/src/index.ts

@@ -202,6 +202,7 @@ export class SessionController extends TypertRemoteService {
       return Promise.resolve({
         meta: attached.header,
         inheritedEventCount: attached.inheritedEventCount,
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         events: attached.snapshotEvents(),
       })
     }

+ 1 - 0
packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts

@@ -41,6 +41,7 @@ async function commandHarness(
   // never appends one: the identity folds as continuable, but from the
   // inherited prefix rather than this Session's own suffix.
   const lineage = childMode === 'seeded-continuable' || childMode === 'seed-only'
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     ? ancestor.snapshotEvents()
     : undefined
   const session = ctx.sessions.create(sessionId, {

+ 3 - 0
packages/api/session-controller/tests/session-cold.host.spec.ts

@@ -203,6 +203,7 @@ describe('attached updatedAt tracks human prompts', () => {
       meta: { cwd: '/proj', createdAt: 500 },
     })
     ctx.agents.register({ id: resumed.id, session: resumed, status: 'idle', ctx } as Agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const boundary = resumed.snapshotEvents().at(-1)
     expect(boundary?.type).toBe('session/end-seed')
     expect(boundary?.time).toBeGreaterThan(worked)
@@ -717,6 +718,7 @@ describe('sessions.prompt synchronous rejection', () => {
       defaultModelSelection: () => ({ provider: 'p', model: 'm' }),
       cwd: '/tmp',
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const initialEvents = session.snapshotEvents()
     const rejectedContent: readonly SessionPromptRequest['content'][] = [
       [],
@@ -741,6 +743,7 @@ describe('sessions.prompt synchronous rejection', () => {
     }
     expect(followup).not.toHaveBeenCalled()
     expect(steer).not.toHaveBeenCalled()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents()).toEqual(initialEvents)
 
     const queued = await remote.prompt(promptRequest({

+ 6 - 0
packages/api/session-controller/tests/session-fork.host.spec.ts

@@ -95,6 +95,7 @@ describe('sessions.fork', () => {
     expect(response.ok ? null : response.error).toBeNull()
     if (!response.ok) return
     const child = ctx.sessions.get(response.value.sessionId)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child?.snapshotEvents().map(event => event.type)).toEqual([
       'turn/start', 'user/message', 'turn/end', 'session/end-seed',
     ])
@@ -214,12 +215,14 @@ describe('sessions.fork', () => {
     const omitted = await proxy.fork(request({ sessionId: source.id }))
     expect(omitted.ok).toBe(true)
     if (omitted.ok) {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(ctx.sessions.get(omitted.value.sessionId)?.snapshotEvents().map(event => event.type))
         .toEqual(expectedTypes)
     }
     const pastEnd = await proxy.fork(request({ sessionId: source.id, atSeq: 999 }))
     expect(pastEnd.ok).toBe(true)
     if (pastEnd.ok) {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(ctx.sessions.get(pastEnd.value.sessionId)?.snapshotEvents().map(event => event.type))
         .toEqual(expectedTypes)
     }
@@ -243,10 +246,12 @@ describe('sessions.fork', () => {
     const source = liveAgent(ctx, 'session-aborted', 1, 'aborted')
     // What a stopped message's fork button anchors on: the frozen node sits
     // one event before its turn/end, floored client-side to that event's seq.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const anchor = (source.snapshotEvents().at(-1)?.seq ?? 0) - 1
     const response = await remote(ctx).fork(request({ sessionId: source.id, atSeq: anchor }))
     expect(response.ok).toBe(true)
     if (!response.ok) return
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(ctx.sessions.get(response.value.sessionId)?.snapshotEvents().map(event => event.type)).toEqual([
       'turn/start', 'user/message', 'turn/end',
       'turn/start', 'user/message', 'turn/end',
@@ -258,6 +263,7 @@ describe('sessions.fork', () => {
   it('rejects an in-log anchor whose turn is still open', async () => {
     const ctx = await composed()
     const source = liveAgent(ctx, 'session-open', 1, 'open')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const anchor = source.snapshotEvents().at(-1)?.seq ?? 0
     const response = await remote(ctx).fork(request({ sessionId: source.id, atSeq: anchor }))
     expect(response).toMatchObject({

+ 3 - 0
packages/api/session-controller/tests/session-projections.host.spec.ts

@@ -172,6 +172,7 @@ describe('session.history projections block', () => {
     parent.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
     const inheritedEventCount = parent.seq
     const child = ctx.sessions.create(SessionId('wire-seed-child'), {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: parent.snapshotEvents(),
       inheritedEventCount,
       meta: {
@@ -448,6 +449,7 @@ describe('session.history projections block', () => {
     expect('test/last-user' in after.projections.values).toBe(false)
     expect(after.projections.values.sessionListMetadata).toEqual({
       blank: true,
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       lastPromptAt: session.eventAt(SessionSeq(session.seq - 1))?.time,
     })
   })
@@ -482,6 +484,7 @@ describe('session.list projections column', () => {
     expect(row?.projections?.values['test/last-user']).toEqual({ text: 'm0' })
     expect(row?.projections?.values.sessionListMetadata).toEqual({
       blank: false,
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       lastPromptAt: session.eventAt(SessionSeq(session.seq - 1))?.time,
     })
     expect(row?.projections?.asOfSeq).toBe(session.seq - 1)

+ 1 - 0
packages/api/session-controller/tests/session-rename.host.spec.ts

@@ -76,6 +76,7 @@ describe('sessions.rename', () => {
     expect(renamed.ok).toBe(true)
     if (!renamed.ok) return
     expect(renamed.value.title).toBe('new name')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = source.snapshotEvents().findLast(item => item.type === 'session/title')
     expect(event?.seq).toBe(renamed.value.seq)
     expect(event?.data).toMatchObject({ title: 'new name', source: { kind: 'user' } })

+ 1 - 0
packages/api/session-controller/tests/transport.host.spec.ts

@@ -230,6 +230,7 @@ describe('SessionHistoryController', () => {
     observed.resolve({
       source: 'live',
       header: attached.header,
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       events: attached.snapshotEvents(),
       cursor: attached.seq - 1,
       projections: { asOfSeq: attached.seq - 1, values: {} },

+ 1 - 0
packages/bundle/headless/src/index.ts

@@ -67,6 +67,7 @@ function summarize(session: Session, firstSeq: SessionLogOffset): RunOutcome {
   let reason: SessionEvent<'turn/end'>['data']['reason'] | undefined
   const length = session.seq
   for (let seq = firstSeq; seq < length; seq++) {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = session.eventAt(SessionSeq(seq))
     if (event === undefined) {
       throw new Error(`headless summary cannot read seq ${String(seq)} below captured length ${String(length)}`)

+ 1 - 0
packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts

@@ -70,6 +70,7 @@ async function executeShell(text: string, nested: boolean, name = 'bash', maxInl
     let block: ToolResultNode
     if (nested) {
       expect(result.value).toMatchObject({ result: true })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const dispatches = session.snapshotEvents().filter(event => event.type === 'tool/ptc-dispatch')
       expect(dispatches).toHaveLength(1)
       const event = dispatches[0]!

+ 1 - 0
packages/compaction/command-compact/tests/command-compact.spec.ts

@@ -121,6 +121,7 @@ function expectLastLifecycle(
   args: string,
   outcome: CommandResult,
 ): string {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   const lifecycle = test.agent.session.snapshotEvents()
     .filter(event => event.type === 'command/run' || event.type === 'command/done')
     .slice(-2)

+ 1 - 0
packages/compaction/command-compact/tests/loader-composition.spec.ts

@@ -130,6 +130,7 @@ describe('command-compact real Loader composition', () => {
       text: 'Compacted 3 history items (~99 tokens).',
       sourceEventSeq: RESULT.summarySeq,
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().map(event => ({ type: event.type, data: event.data }))).toEqual([
       {
         type: 'command/run',

+ 6 - 3
packages/compaction/compaction-basic/src/region.ts

@@ -98,7 +98,8 @@ interface TransactionFailure {
  */
 function systemHead(session: Session, headSeq: SessionSeq): SessionEvent<'system/message'> | undefined {
   // Surface nodes are current log seqs, so the event exists.
-  // oxlint-disable-next-line typescript/no-non-null-assertion
+  // Existing Session history read; migration deferred.
+  // oxlint-disable-next-line typescript/no-non-null-assertion, typescript/no-deprecated
   const head = session.eventAt(headSeq)!
   return head.type === 'system/message' ? head : undefined
 }
@@ -536,7 +537,8 @@ function buildSummarizationInput(
   const system = head === undefined ? null : session.deriveEventMessage(head)
   const regionMessages = shadowedSeqs
     // shadowedSeqs are current surface seqs, so each is a valid log index.
-    // oxlint-disable-next-line typescript/no-non-null-assertion
+    // Existing Session history read; migration deferred.
+    // oxlint-disable-next-line typescript/no-non-null-assertion, typescript/no-deprecated
     .map(seq => session.deriveEventMessage(session.eventAt(seq)!))
     .filter((message): message is Message => message !== null)
   return {
@@ -553,7 +555,8 @@ function inspectCompactionEntryState(session: Session): CompactionEntryState {
   let compactionEntryStateKnown = false
   let latestEndSeedSeq: SessionSeq | undefined
   for (let seq = session.seq - 1; seq >= 0; seq -= 1) {
-    // oxlint-disable-next-line typescript/no-non-null-assertion
+    // Existing Session history read; migration deferred.
+    // oxlint-disable-next-line typescript/no-non-null-assertion, typescript/no-deprecated
     const event = session.eventAt(SessionSeq(seq))!
     if (latestEndSeedSeq === undefined && event.type === 'session/end-seed') {
       latestEndSeedSeq = event.seq

+ 29 - 0
packages/compaction/compaction-basic/tests/compaction-basic.spec.ts

@@ -625,6 +625,7 @@ describe('pressure measurement and retention', () => {
 
     await expect(compactIfNeeded(compact, session, 'context-overflow')).resolves.toBeNull()
     expect(session.surface.replaceGeneration).toBe(generation)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/start')).toBe(false)
   })
 
@@ -646,6 +647,7 @@ describe('pressure measurement and retention', () => {
     const compact = service(compactConfig, ctx)
     const session = conversation(4, undefined, 'SYSTEM HEAD')
     const [head, firstUser] = session.surface.nodes
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.eventAt(head!)?.type).toBe('system/message')
 
     const range = selectCompactableRange(session, ctx.tokenMeter.measure(session), compactConfig.retainTokens!)
@@ -889,9 +891,11 @@ describe('optional model-free tool-result pruning', () => {
 
     expect(await compactIfNeeded(compact, session)).not.toBeNull()
     expect(compact.calls).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const original = session.snapshotEvents().find(event => event.type === 'tool/result')
     expect(original?.type === 'tool/result' && original.data.message.content[0].content[0])
       .toEqual({ type: 'text', text: 'X'.repeat(3_000) })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().filter(event =>
       event.type === 'tool/result' && event.surfaceOp !== 'append')).toHaveLength(0)
   })
@@ -918,6 +922,7 @@ describe('compaction region transaction', () => {
     expect(result.shadowedTokenCount).toBeGreaterThan(0)
     expect(compact.calls[0]).toMatchObject({ signal: SIGNAL })
     expect(summarizedText(compact.calls[0]!.input)).toContain('fixture user 1')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const summary = session.snapshotEvents().findLast(event => event.type === 'compaction/summary')
     expect(summary?.data).toMatchObject({
       shadowedSeqs: result.shadowedSeqs,
@@ -934,6 +939,7 @@ describe('compaction region transaction', () => {
     expect(head.content[0]?.type === 'text' ? head.content[0].text : '').toContain('<compacted-summary>')
     expect(head.content.at(-1)).toEqual({ type: 'text', text: '</compacted-summary>' })
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const replay = Session.create(SessionId('replay'), session.snapshotEvents())
     expect(replay.deriveMessages()).toEqual(session.deriveMessages())
   })
@@ -958,6 +964,7 @@ describe('compaction region transaction', () => {
     const compact = service()
     const emptyHead = conversation(3, undefined, '')
     const emptyNodes = emptyHead.surface.nodes
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(emptyHead.eventAt(emptyNodes[0]!)?.type).toBe('system/message')
     await compact.compactRegion(emptyNodes[1]!, emptyNodes[2]!, agent(emptyHead, MODEL), SIGNAL)
     expect(compact.calls[0]!.input.messages[0]).toMatchObject({ role: 'user' })
@@ -979,6 +986,7 @@ describe('compaction region transaction', () => {
       .rejects.toThrow(/node 0 holds the system prompt/)
 
     expect(session.surface.nodes).toEqual(nodes)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = session.snapshotEvents().findLast(event => event.type === 'compaction/end')
     expect(end?.type === 'compaction/end' && end.data.error).toBeDefined()
   })
@@ -1092,6 +1100,7 @@ describe('compaction region transaction', () => {
       agent(session, MODEL),
     )).rejects.toThrow('summary unavailable')
     expect(session.surface.nodes).toEqual(before)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().findLast(event => event.type === 'compaction/end')?.data)
       .toMatchObject({ error: 'summary unavailable' })
   })
@@ -1106,6 +1115,7 @@ describe('compaction region transaction', () => {
       nodes[2]!,
       agent(session, MODEL),
     )).rejects.toBe('plain failure')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().findLast(event => event.type === 'compaction/end')?.data)
       .toMatchObject({ error: 'plain failure' })
   })
@@ -1126,6 +1136,7 @@ describe('compaction region transaction', () => {
       nodes[2]!,
       agent(session, MODEL),
     )).resolves.toMatchObject({ shadowedSeqs: nodes.slice(0, 3) })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(true)
   })
 
@@ -1145,6 +1156,7 @@ describe('compaction region transaction', () => {
       nodes[2]!,
       agent(session, MODEL),
     )).rejects.toThrow(/session surface changed/)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(false)
   })
 
@@ -1162,6 +1174,7 @@ describe('compaction region transaction', () => {
       nodes[2]!,
       agent(session, MODEL),
     )).rejects.toThrow(/summary is not smaller/)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(false)
   })
 
@@ -1435,6 +1448,7 @@ describe('default one-shot summarizer', () => {
     const session = conversation(3, 'large history '.repeat(500))
     const nodes = session.surface.nodes
     await compact.compactRegion(nodes[0]!, nodes[3]!, agent(session, MODEL), SIGNAL)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().findLast(event => event.type === 'compaction/summary')?.data).toMatchObject({
       summary: [{ type: 'text', text: 'routed summary' }],
       llmStreamCall: true,
@@ -1561,6 +1575,7 @@ describe('automatic listener and loader composition', () => {
     next: () => Promise<RequestErrorAction> = () => Promise.resolve(undefined),
   ): Promise<boolean> {
     const failure: LlmFailure = { message: error.message, code: error.code ?? 'UNKNOWN' }
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turn = owner.session.snapshotEvents().findLast(event => event.type === 'turn/start')?.data.turn ?? 1
     return agentEvents(ctx, owner).waterfall(
       'agent/request-error',
@@ -1581,10 +1596,12 @@ describe('automatic listener and loader composition', () => {
     })
     const pressured = conversation(4)
     await preStep(ctx, agent(pressured, 'unconfigured-agent-fallback'))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(pressured.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(true)
 
     const small = conversation(1)
     await preStep(ctx, agent(small, MODEL))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(small.snapshotEvents().some(event => event.type === 'compaction/start')).toBe(false)
     expect(compact.calls).toHaveLength(1)
   })
@@ -1602,6 +1619,7 @@ describe('automatic listener and loader composition', () => {
       .resolves.toEqual({ kind: 'enter', messages: [] })
 
     expect(compactIfNeeded).not.toHaveBeenCalled()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(pressured.snapshotEvents().some(event => event.type === 'compaction/start')).toBe(false)
   })
 
@@ -1618,6 +1636,7 @@ describe('automatic listener and loader composition', () => {
 
     await expect(preStep(ctx, agent(session, MODEL))).resolves.toEqual({ kind: 'enter', messages: [] })
     expect(warnings).toContainEqual(expect.stringContaining('temporary failure'))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(false)
   })
 
@@ -1677,6 +1696,7 @@ describe('automatic listener and loader composition', () => {
 
     expect(decision).toBe(true)
     expect(session.surface.replaceGeneration).toBe(beforeGeneration + 1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(true)
     expect(session.surface.nodes).toContain(retainedSeq)
   })
@@ -1696,6 +1716,7 @@ describe('automatic listener and loader composition', () => {
 
     expect(await recover(ctx, agent(session, MODEL), overflow())).toBe(true)
     expect(session.surface.replaceGeneration).toBe(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(false)
     expect(compact.calls).toHaveLength(0)
   })
@@ -1714,6 +1735,7 @@ describe('automatic listener and loader composition', () => {
     const session = toolConversation()
 
     expect(await recover(ctx, agent(session, MODEL), overflow())).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(true)
     expect(compact.calls).toHaveLength(1)
     expect(summarizedText(compact.calls[0]!.input)).toContain('tool result middle pruned')
@@ -1737,7 +1759,9 @@ describe('automatic listener and loader composition', () => {
 
     expect(await recover(ctx, agent(session, MODEL), overflow())).toBe(true)
     expect(session.surface.replaceGeneration).toBe(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().filter(event => event.type === 'tool/result')).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().findLast(event => event.type === 'compaction/end')?.data)
       .toMatchObject({ error: 'summary unavailable after prune' })
     expect(warnings).toContainEqual(expect.stringContaining('retrying from the replacement surface'))
@@ -1940,9 +1964,11 @@ describe('automatic listener and loader composition', () => {
     })
     const session = conversation(4)
     await preStep(ctx, agent(session, MODEL))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const summaries = session.snapshotEvents().filter(event => event.type === 'compaction/summary').length
     expect(summaries).toBe(1)
     expect(await recover(ctx, agent(session, MODEL), overflow())).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().filter(event => event.type === 'compaction/summary')).toHaveLength(summaries)
   })
 
@@ -1955,6 +1981,7 @@ describe('automatic listener and loader composition', () => {
     })
     const session = conversation(4)
     await preStep(ctx, agent(session, MODEL))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/start')).toBe(false)
     expect(await recover(ctx, agent(session, MODEL), overflow())).toBe(false)
   })
@@ -1987,6 +2014,7 @@ describe('automatic listener and loader composition', () => {
 
     const session = conversation(4)
     await preStep(ctx, agent(session, MODEL))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/start')).toBe(false)
     expect(await recover(ctx, agent(session, MODEL), overflow())).toBe(false)
   })
@@ -2128,6 +2156,7 @@ describe('route-priced image pressure', () => {
 
     const result = await compact.compactIfNeeded(agent(session), 'pressure', SIGNAL)
     expect(result).not.toBeNull()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const summaryEvent = session.snapshotEvents().find(event => event.type === 'compaction/summary')
     expect(summaryEvent).toBeDefined()
     const shadowedHeuristic = before.nodes

+ 12 - 0
packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts

@@ -212,6 +212,7 @@ function overflowHistorySeed(): readonly SessionEvent[] {
     session.append('step/end', { turn, step: 1 })
     session.append('turn/end', { turn, reason: { kind: 'completed' } })
   }
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return session.snapshotEvents()
 }
 
@@ -230,7 +231,9 @@ describe('CBR-001: a real-loop checkpoint is a valid boundary on both sides', ()
       await waitForIdle(ctx, agent)
 
       expect(agent.session.requestHeader()?.config.model).toBe('mock')
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(true)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().at(-1)).toMatchObject({
         type: 'turn/end',
         data: { reason: { kind: 'completed' } },
@@ -247,6 +250,7 @@ describe('CBR-001: a real-loop checkpoint is a valid boundary on both sides', ()
       agent.followup(createUserMessage({ content: [{ type: 'text', text: 'do tool work' }], source: { kind: 'user' } }))
       await waitForIdle(ctx, agent)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const events = agent.session.snapshotEvents()
       const compactStart = events.find(event => event.type === 'compaction/start')
       expect(compactStart).toBeDefined()
@@ -279,6 +283,7 @@ describe('CBR-001: a real-loop checkpoint is a valid boundary on both sides', ()
       agent.followup(createUserMessage({ content: [{ type: 'text', text: 'do a long multi-step task' }], source: { kind: 'user' } }))
       await waitForIdle(ctx, agent)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const events = agent.session.snapshotEvents()
       // A compaction ran: at least one checkpoint landed on the surface.
       const checkpoints = events.filter(
@@ -347,6 +352,7 @@ describe('token pressure after loop-admitted system prompts', () => {
         prompt = nextPrompt
         agent.followup(createUserMessage({ content: [{ type: 'text', text: 'question' }], source: { kind: 'user' } }))
         await agent.whenIdle()
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         expect(agent.session.snapshotEvents().at(-1)).toMatchObject({
           type: 'turn/end', data: { reason: { kind: 'completed' } },
         })
@@ -354,11 +360,13 @@ describe('token pressure after loop-admitted system prompts', () => {
         expect(measured.baseline).toEqual({ kind: 'usage', tokens: 1110, usage })
         expect(measured.surfaceDeltaTokens).toBe(0)
         expect(measured.totalTokens).toBe(1110)
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         const replay = Session.create(SessionId('prompt-pressure-replay'), agent.session.snapshotEvents())
         expect(ctx.tokenMeter.measure(replay)).toMatchObject({
           baseline: measured.baseline, surfaceDeltaTokens: 0, totalTokens: 1110, nodes: measured.nodes,
         })
       }
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const events = agent.session.snapshotEvents()
       const prompts = events.filter(event => event.type === 'system/message')
       expect(prompts[0]?.surfaceOp).toBe('append')
@@ -428,6 +436,7 @@ describe('context-overflow recovery across the real loop and compaction-basic',
         expect(retry).toContain('RECOVERY CHECKPOINT')
         expect(retry).not.toContain('OLD HISTORY SENTINEL')
 
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         const events = agent.session.snapshotEvents()
         const stepStart = events.find(event =>
           event.type === 'step/start' && event.data.turn === 3 && event.data.step === 1,
@@ -490,10 +499,13 @@ describe('context-overflow recovery across the real loop and compaction-basic',
 
       expect(adapter.conversationRequests).toHaveLength(3)
       expect(adapter.summaryRequests).toHaveLength(1)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().filter(event => event.type === 'llm/retry').map(event => event.data))
         .toEqual([expect.objectContaining({ turn: 3, step: 1, retry: 1, failure: { message: 'temporary provider outage', code: 'SERVER' } })])
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start').slice(-1).map(event => event.data.turn))
         .toEqual([3])
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().at(-1)).toMatchObject({
         type: 'turn/end',
         data: { reason: { kind: 'completed' } },

+ 29 - 0
packages/compaction/compaction-basic/tests/manual-compaction.spec.ts

@@ -233,6 +233,7 @@ function detachedService(): { ctx: Context; compact: GatedCompactionEngine; flus
 }
 
 function compactEvents(session: Session): SessionEvent[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return session.snapshotEvents().filter(event => event.type.startsWith('compaction/'))
 }
 
@@ -295,13 +296,16 @@ describe('compactNow through the real loop', () => {
     const result = await compact.compactNow(agent, SIGNAL)
 
     expect(result).not.toBeNull()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const start = agent.session.snapshotEvents().findLast(event => event.type === 'compaction/start')
     const injected = agent.inbox.nextStep.find(message =>
       message.source.kind === 'plugin' && message.source.plugin === 'test')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().findLast(event => event.type === 'compaction/end')
     expect(start).toBeDefined()
     expect(injected).toBeDefined()
     expect(end).toBeDefined()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'user/message'
       && event.data.id === injected?.id)).toBe(false)
 
@@ -336,6 +340,7 @@ describe('compactNow through the real loop', () => {
     expect(result).not.toBeNull()
     expect(agent.session.deriveMessages()[0]?.role).toBe('system')
     expect(derivedText(agent.session)[1]).toContain('checkpoint')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'user/message'
       && event.data.source.kind === 'plugin' && event.data.source.plugin === 'listener')).toHaveLength(0)
     const types = compactEvents(agent.session).map(event => event.type)
@@ -356,6 +361,7 @@ describe('compactNow through the real loop', () => {
 
     await agent.whenIdle()
     expect(adapter.requests).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'compaction/start')).toBe(false)
   })
 
@@ -406,13 +412,18 @@ describe('compactNow transaction and failure classification', () => {
     expect(result).not.toBeNull()
     expect(result?.sourceCommandId).toBe(commandId)
     expect(flushes()).toBe(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().filter(event => event.type === 'turn/start').at(-1)?.data.turn).toBe(7)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const start = session.snapshotEvents().findLast(event => event.type === 'compaction/start')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const summaryEvent = session.snapshotEvents().findLast(event => event.type === 'compaction/summary')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const checkpoint = session.snapshotEvents().findLast(
       (event): event is SessionEvent<'user/message'> => event.type === 'user/message'
         && isCompactCheckpointSource(event.data.source),
     )
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = session.snapshotEvents().findLast(event => event.type === 'compaction/end')
     const correlated = { compactionId: result?.compactionId, sourceCommandId: commandId }
     expect(start?.data).toEqual({ ...correlated, turn: null })
@@ -443,8 +454,11 @@ describe('compactNow transaction and failure classification', () => {
       compactionId: CompactionId('stale-manual-compaction'),
       turn: null,
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const reloaded = Session.create(SessionId('stale-orphan'), original.snapshotEvents())
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const boundary = reloaded.snapshotEvents().findLast(event => event.type === 'session/end-seed')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const orphan = reloaded.snapshotEvents().find(event => event.type === 'compaction/start')
     const agent = fakeAgent(reloaded, () => () => undefined)
 
@@ -462,6 +476,7 @@ describe('compactNow transaction and failure classification', () => {
     })
     original.append('turn/start', { turn: 3 })
     original.append('turn/end', { turn: 3, reason: { kind: 'interrupted' } })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const reloaded = Session.create(SessionId('reloaded-orphan'), original.snapshotEvents())
     const agent = fakeAgent(reloaded, () => () => undefined)
 
@@ -565,6 +580,7 @@ describe('compactNow transaction and failure classification', () => {
     expect(session.surface.replaceGeneration).toBe(generation + 1)
     expect(session.surface.nodes).not.toContain(head)
     expect(compactEvents(session).map(event => event.type)).toEqual(['compaction/start', 'compaction/end'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'user/message'
       && isCompactCheckpointSource(event.data.source))).toBe(false)
   })
@@ -584,6 +600,7 @@ describe('compactNow transaction and failure classification', () => {
     expect(causeOf(error).message).toBe('boundary rejected')
     vi.restoreAllMocks()
     expect(flushes()).toBe(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().findLast(event => event.type.startsWith('compaction/'))?.type)
       .toBe('compaction/summary')
     expect(compactEvents(session).filter(event => event.type === 'compaction/start')).toHaveLength(1)
@@ -650,6 +667,7 @@ describe('compactNow transaction and failure classification', () => {
     vi.restoreAllMocks()
     expect(error.code).toBe('commit')
     expect(released).toBe(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = session.snapshotEvents().findLast(event => event.type === 'compaction/end')
     expect(end?.type === 'compaction/end' && end.data.error).toContain('summary record rejected')
     expect(end?.type === 'compaction/end' && end.data.turn).toBeNull()
@@ -686,7 +704,9 @@ describe('compactNow transaction and failure classification', () => {
     const result = await compact.compactNow(agent, SIGNAL)
 
     expect(result).not.toBeNull()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().find(event => event.type === 'compaction/start')?.data)
       .toEqual({ compactionId: result?.compactionId, turn: null })
   })
@@ -699,8 +719,11 @@ describe('compactNow transaction and failure classification', () => {
 
     expect((await rejection(compact.compactNow(agent, SIGNAL))).code).toBe('persistence')
     vi.restoreAllMocks()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const start = session.snapshotEvents().findLast(event => event.type === 'compaction/start')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = session.snapshotEvents().findLast(event => event.type === 'compaction/end')
     expect(end?.data).toEqual({ compactionId: start?.data.compactionId, turn: null })
   })
@@ -717,6 +740,7 @@ describe('compactNow transaction and failure classification', () => {
       const reserve = vi.fn(() => testCase.release)
       const measure = vi.spyOn(ctx.tokenMeter, 'measure')
       const agent = fakeAgent(testCase.session, reserve)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const before = testCase.session.snapshotEvents()
       const reason = Object.freeze({ kind: 'cancelled', case: testCase.name })
       const controller = new AbortController()
@@ -732,6 +756,7 @@ describe('compactNow transaction and failure classification', () => {
       expect(reserve).not.toHaveBeenCalled()
       expect(measure).not.toHaveBeenCalled()
       expect(compact.calls).toHaveLength(0)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(testCase.session.snapshotEvents()).toEqual(before)
       vi.restoreAllMocks()
     }
@@ -781,6 +806,7 @@ describe('compactNow transaction and failure classification', () => {
 
     await expect(compact.compactNow(agent, controller.signal)).rejects.toBe(reason)
     expect(compactEvents(session).map(event => event.type)).toEqual(['compaction/start', 'compaction/end'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().some(event => event.type === 'compaction/summary')).toBe(false)
   })
 
@@ -825,6 +851,7 @@ describe('compactNow transaction and failure classification', () => {
 
     await compact.compactNow(agent, SIGNAL)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const summary = session.snapshotEvents().find(event => event.type === 'compaction/summary')
     expect(summary?.type === 'compaction/summary' && summary.data.rawOutput).toEqual(compact.rawOutput)
     expect(summary?.type === 'compaction/summary' && summary.data.usage).toEqual(compact.usage)
@@ -840,7 +867,9 @@ describe('compactNow transaction and failure classification', () => {
 
     await compact.compactNow(agent, SIGNAL)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const start = session.snapshotEvents().findLast(event => event.type === 'compaction/start')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = session.snapshotEvents().findLast(event => event.type === 'compaction/end')
     expect(start).toBeDefined()
     expect(end).toBeDefined()

+ 1 - 0
packages/compaction/compaction-tool-result-pruner/src/index.ts

@@ -136,6 +136,7 @@ export class ToolResultPruner extends Service {
   pruneSession(session: Session): PruneResult {
     const candidates: SnapshotCandidate[] = []
     for (const seq of [...session.surface.nodes]) {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const event = session.eventAt(seq)
       /* v8 ignore next -- surface seqs are validated contiguous log references. */
       if (event?.type === 'tool/result') candidates.push({ seq, event })

+ 4 - 0
packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts

@@ -186,7 +186,9 @@ describe('ToolResultPruner session transaction', () => {
     expect(entry).toMatchObject({ originalSeq, callId: ToolCallId('one'), charsBefore: 100 })
     expect(entry.charsAfter).toBeLessThanOrEqual(50)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const original = session.snapshotEvents()[originalSeq]!
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const replacement = session.snapshotEvents()[entry.replacementSeq]! as SurfaceEvent
     expect(original).toMatchObject({
       type: 'tool/result',
@@ -221,6 +223,7 @@ describe('ToolResultPruner session transaction', () => {
     // Shadow-price protocol: the metering event sits directly before the
     // replacement and prices the shadowed node with the shared estimator.
     if (original.type !== 'tool/result') throw new Error('original is not a tool/result')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents()[entry.replacementSeq - 1]).toMatchObject({
       type: 'compaction/prune',
       data: {
@@ -256,6 +259,7 @@ describe('ToolResultPruner session transaction', () => {
       turn: 2,
     })
     service().pruneSession(session)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const replay = Session.create(session.id, session.snapshotEvents())
     expect(replay.deriveMessages()).toEqual(session.deriveMessages())
     expect(replay.surface.replaceGeneration).toBe(session.surface.replaceGeneration)

+ 1 - 0
packages/compaction/compaction/src/invariant.ts

@@ -298,6 +298,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant
       surface: new SurfaceManager(surfaceEvents),
     }
     traces.set(session, trace)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = session.snapshotEvents()
     const staleOrphanStartSeqs = inheritedOrphanStartSeqs(events)
     for (const event of events) {

+ 1 - 0
packages/compaction/compaction/src/tool-pairing.ts

@@ -50,6 +50,7 @@ function extendCache(
   const pendingCuts: boolean[] = []
   let inProgressToolCalls = cache.inProgressToolCalls
   for (const seq of tail) {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = session.eventAt(seq)
     if (event === undefined || event.seq !== seq) {
       throw new Error(`tool-pairing balance: surface seq ${seq} has no matching session event (corrupt surface)`)

+ 3 - 0
packages/compaction/compaction/tests/compaction.spec.ts

@@ -131,6 +131,7 @@ describe('CompactionEngine seam', () => {
 
     const result = await svc.compactRegion(original.seq, original.seq, stubAgent(session, 'm'))
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const startEvent = session.snapshotEvents().find(e => e.type === 'compaction/start')
     expect(startEvent).toBeDefined()
     // Log-only: the compiler rejects surfaceOp on compaction/* (not a SurfaceEventType);
@@ -142,12 +143,14 @@ describe('CompactionEngine seam', () => {
     expect(result.endSeq).toBeGreaterThan(result.summarySeq)
     expect(result.shadowedRange).toEqual({ start: original.seq, end: original.seq })
     expect(result.shadowedSeqs).toEqual([original.seq])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const checkpoint = session.snapshotEvents().find(event => event.type === 'user/message'
       && isCompactCheckpointSource(event.data.source))
     expect(checkpoint?.type === 'user/message' && checkpoint.data.source)
       .toEqual(compactCheckpointSource(result.compactionId))
     expect(isCompactCheckpointSource({ kind: 'plugin', plugin: 'other' })).toBe(false)
     expect(isCompactCheckpointSource({ kind: 'user' })).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().filter(e => e.type.startsWith('compaction/')).map(e => e.type))
       .toEqual(['compaction/start', 'compaction/summary', 'compaction/end'])
   })

+ 8 - 0
packages/compaction/compaction/tests/invariant.spec.ts

@@ -134,8 +134,10 @@ describe('compaction invariants', () => {
     const source = Session.create(SessionId('stale-compaction-source'))
     source.append('compaction/start', { compactionId: TEST_COMPACTION_ID, turn: null })
     const replayed = ctx.sessions.create(SessionId('stale-compaction-replay'), {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: source.snapshotEvents(),
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(replayed.snapshotEvents().map(event => event.type))
       .toEqual(['compaction/start', 'session/end-seed'])
 
@@ -155,8 +157,10 @@ describe('compaction invariants', () => {
     startTurn(source)
     source.append('compaction/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
     const replayed = ctx.sessions.create(SessionId('stale-numbered-compaction-replay'), {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: source.snapshotEvents(),
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(replayed.snapshotEvents().map(event => event.type))
       .toEqual(['turn/start', 'compaction/start', 'session/end-seed'])
 
@@ -177,8 +181,10 @@ describe('compaction invariants', () => {
     startTurn(source)
     source.append('turn/end', { turn: 1, reason: { kind: 'interrupted' } })
     const replayed = ctx.sessions.create(SessionId('stale-repaired-compaction-replay'), {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: source.snapshotEvents(),
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(replayed.snapshotEvents().map(event => event.type)).toEqual([
       'compaction/start',
       'turn/start',
@@ -204,8 +210,10 @@ describe('compaction invariants', () => {
     source.append('turn/end', { turn: 1, reason: { kind: 'interrupted' } })
     source.append('compaction/end', { compactionId: TEST_COMPACTION_ID, turn: null, error: 'failed after crossing turn' })
     const replayed = ctx.sessions.create(SessionId('closed-nested-compaction-replay'), {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: source.snapshotEvents(),
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(replayed.snapshotEvents().at(-1)?.type).toBe('session/end-seed')
 
     await ctx.plugin(InvariantRegistry)

+ 1 - 0
packages/compaction/compaction/tests/tool-pairing.spec.ts

@@ -7,6 +7,7 @@ import type { SessionEvent, SessionSeq as SessionSeqType } from '@deepseek-ai/ds
 const SURFACE = { surfaceOp: 'append' as const }
 
 function seqOf(session: Session, type: SessionEvent['type'], nth = 0): SessionSeqType {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return session.snapshotEvents().filter(event => event.type === type)[nth]!.seq
 }
 

+ 2 - 0
packages/context/agent-instructions/src/index.ts

@@ -53,6 +53,7 @@ function visibleBaselineSource(
     }
   }
   for (const seq of agent.session.surface.nodes.toReversed()) {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = agent.session.eventAt(seq)
     if (event?.type === 'user/message'
       && event.data.source.kind === 'agent-instructions'
@@ -228,6 +229,7 @@ export function apply(ctx: Context, config: Config): void {
     const alreadySupplied = desired !== undefined && (
       claimed.some(message => sameContextPayload(message, desired))
       || agent.session.surface.nodes.some((seq) => {
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         const event = agent.session.eventAt(seq)
         return event?.type === 'user/message' && sameContextPayload(event.data, desired)
       })

+ 1 - 0
packages/context/agent-instructions/src/state.ts

@@ -139,6 +139,7 @@ function visibleInstructionChanges(
 ): Map<string, AgentInstructionChange> {
   const visible = new Map<string, AgentInstructionChange>()
   for (const seq of agent.session.surface.nodes) {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = agent.session.eventAt(seq)
     if (event?.type !== 'user/message' || !isWorkspaceContextSource(event.data.source)) continue
     const changes = workspaceInstructionChanges(event.data.source)

+ 3 - 0
packages/context/agent-instructions/tests/agent-instructions.e2e.ts

@@ -77,6 +77,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('workspace context e2e: real mode
     live.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'Workspace context handshake?' }], source: { kind: 'user' } }))
     await waitForIdle(live.ctx, live.agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(finalText(live.agent.session.snapshotEvents())).toContain(PROBE)
   }, 120_000)
 
@@ -89,6 +90,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('workspace context e2e: real mode
     live.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'Use the read tool to inspect pkg/deep/file.txt. After reading it, answer: nested instruction handshake?' }], source: { kind: 'user' } }))
     await waitForIdle(live.ctx, live.agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(finalText(live.agent.session.snapshotEvents())).toContain(NESTED_PROBE)
   }, 120_000)
 
@@ -102,6 +104,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('workspace context e2e: real mode
     live.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'You must use the read tool to inspect trigger.txt. After reading it, answer: updated workspace context handshake?' }], source: { kind: 'user' } }))
     await waitForIdle(live.ctx, live.agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = live.agent.session.snapshotEvents()
     const update = events.find(event => event.type === 'user/message'
       && event.data.source.kind === 'agent-instructions'

+ 34 - 0
packages/context/agent-instructions/tests/agent-instructions.spec.ts

@@ -277,6 +277,7 @@ async function syncedWorkspaceContext(ctx: Context, agent: Agent): Promise<UserM
 }
 
 function baselineEvents(agent: Agent): SessionEvent[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents().filter(event =>
     event.type === 'user/message'
     && event.data.source.kind === 'agent-instructions'
@@ -1127,6 +1128,7 @@ describe('workspace context request injection', () => {
       const second = await composeBaselinePrefix(ctx, agent)
 
       expect(second).toEqual(first)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().filter(event => event.type === 'user/message' && event.data.source.kind !== 'user')).toHaveLength(1)
       expect(derivedText(agent)).toContain('repo rule')
     } finally {
@@ -1146,13 +1148,16 @@ describe('workspace context request injection', () => {
       const original = await stubAgent(root)
       await composeBaselinePrefix(ctx, original)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const firstResume = await stubAgent(root, original.session.snapshotEvents())
       await composeBaselinePrefix(ctx, firstResume)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const secondResume = await stubAgent(root, firstResume.session.snapshotEvents())
       await composeBaselinePrefix(ctx, secondResume)
 
       expect(baselineEvents(firstResume)).toHaveLength(1)
       expect(baselineEvents(secondResume)).toHaveLength(1)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(secondResume.session.snapshotEvents().filter(event => event.type === 'user/message'
         && event.data.source.kind === 'agent-instructions')).toHaveLength(1)
     } finally {
@@ -1175,10 +1180,12 @@ describe('workspace context request injection', () => {
       await composeBaselinePrefix(ctx, original)
 
       fs.throwOnStat.add(join(root, 'AGENTS.md'))
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
       await composeBaselinePrefix(ctx, resumed)
 
       expect(baselineEvents(resumed)).toHaveLength(1)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(resumed.session.snapshotEvents().filter(event => event.type === 'user/message'
         && event.data.source.kind === 'agent-instructions')).toHaveLength(1)
     } finally {
@@ -1201,12 +1208,15 @@ describe('workspace context request injection', () => {
       const original = await stubAgent(cwd)
       await composeBaselinePrefix(ctx, original)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const firstResume = await stubAgent(cwd, original.session.snapshotEvents())
       await composeBaselinePrefix(ctx, firstResume)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const secondResume = await stubAgent(cwd, firstResume.session.snapshotEvents())
       await composeBaselinePrefix(ctx, secondResume)
 
       expect(baselineEvents(secondResume)).toHaveLength(1)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(secondResume.session.snapshotEvents().filter(event => event.type === 'user/message'
         && event.data.source.kind === 'agent-instructions')).toHaveLength(1)
       expect(blocksText(secondResume.session.deriveMessages()[0]?.content)).toContain('omitted AGENTS.md')
@@ -1231,10 +1241,12 @@ describe('workspace context request injection', () => {
       await composeBaselinePrefix(ctx, original)
 
       await write(join(cwd, 'AGENTS.md'), 'package rule')
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(cwd, original.session.snapshotEvents())
       await composeBaselinePrefix(ctx, resumed)
 
       expect(baselineEvents(resumed)).toHaveLength(1)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const update = resumed.session.snapshotEvents().findLast(event => event.type === 'user/message'
         && event.data.source.kind === 'agent-instructions'
         && event.data.source.baseline !== true)
@@ -1268,6 +1280,7 @@ describe('workspace context request injection', () => {
         maxBytes: 65536,
         instructionFileCandidates: ['CLAUDE.md', 'AGENTS.md'],
       })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
       await composeBaselinePrefix(resumedCtx, resumed)
 
@@ -1287,6 +1300,7 @@ describe('workspace context request injection', () => {
         : [])
       expect(new Set(baselineIdentities).size).toBe(2)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const repeated = await stubAgent(root, resumed.session.snapshotEvents())
       await composeBaselinePrefix(resumedCtx, repeated)
       expect(baselineEvents(repeated)).toHaveLength(2)
@@ -1321,6 +1335,7 @@ describe('workspace context request injection', () => {
         maxBytes: 65536,
         instructionFileCandidates: ['CLAUDE.md'],
       })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const claudeResume = await stubAgent(root, original.session.snapshotEvents())
       await composeBaselinePrefix(claudeCtx, claudeResume)
       const claudeBaseline = baselineEvents(claudeResume).at(-1)
@@ -1336,6 +1351,7 @@ describe('workspace context request injection', () => {
         maxBytes: 65536,
         instructionFileCandidates: ['AGENTS.md'],
       })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const restored = await stubAgent(root, claudeResume.session.snapshotEvents())
       await composeBaselinePrefix(restoredCtx, restored)
       const restoredBaseline = baselineEvents(restored).at(-1)
@@ -1371,6 +1387,7 @@ describe('workspace context request injection', () => {
         maxBytes: 65536,
         instructionFileCandidates: ['POLICY.md'],
       })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
       await composeBaselinePrefix(resumedCtx, resumed)
 
@@ -1385,6 +1402,7 @@ describe('workspace context request injection', () => {
         { action: 'remove', scope: sk('.', 'AGENTS.md'), path: 'AGENTS.md' },
       ])
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const repeated = await stubAgent(root, resumed.session.snapshotEvents())
       await composeBaselinePrefix(resumedCtx, repeated)
       expect(baselineEvents(repeated)).toHaveLength(2)
@@ -1415,6 +1433,7 @@ describe('workspace context request injection', () => {
 
       await fiber.dispose()
       await mountWorkspaceContextPlugin(ctx, { dshHome: home, maxBytes: 65536 })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
       agentEvents(ctx, resumed).emit('agent/session-start', { source: 'resume' })
       const claimed = claimInbox(resumed, 'next-step')
@@ -1431,6 +1450,7 @@ describe('workspace context request injection', () => {
 
       expect(decision.messages.map(message => message.id)).toEqual([inserted?.id])
       expect(resumed.inbox.nextStep).toEqual([])
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(resumed.session.snapshotEvents().filter(event => event.type === 'agent/inbox/spliced'
         && event.data.inserted.some(message => message.source.kind === 'agent-instructions'
           && message.source.baseline === true))).toHaveLength(1)
@@ -1461,6 +1481,7 @@ describe('workspace context request injection', () => {
       await write(join(root, 'AGENTS.md'), 'new repo rule')
       await fiber.dispose()
       await mountWorkspaceContextPlugin(ctx, { dshHome: home, maxBytes: 65536 })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
       agentEvents(ctx, resumed).emit('agent/session-start', { source: 'resume' })
       const staleClaim = claimInbox(resumed, 'next-step')
@@ -1514,6 +1535,7 @@ describe('workspace context request injection', () => {
       await originalCtx.fiber.dispose()
       if (provideFs) await resumedCtx.plugin(LocalFileSystem, { cwd: '/' })
       await mountWorkspaceContextPlugin(resumedCtx, { dshHome: home, maxBytes })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
       agentEvents(resumedCtx, resumed).emit('agent/session-start', { source: 'resume' })
       const claimed = claimInbox(resumed, 'next-step')
@@ -1555,6 +1577,7 @@ describe('workspace context request injection', () => {
 
       await composeBaselinePrefix(ctx, agent)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const removal = agent.session.snapshotEvents().find(event => event.type === 'user/message'
         && event.data.source.kind === 'agent-instructions'
         && event.data.source.changes.some(change => change.action === 'remove'))
@@ -1589,6 +1612,7 @@ describe('workspace context request injection', () => {
 
       await composeBaselinePrefix(ctx, agent)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const workspaceEvents = agent.session.snapshotEvents().filter(event => event.type === 'user/message'
         && event.data.source.kind === 'agent-instructions')
       expect(workspaceEvents).toHaveLength(2)
@@ -1598,6 +1622,7 @@ describe('workspace context request injection', () => {
       expect(baselineEvents(agent)).toHaveLength(1)
 
       await composeBaselinePrefix(ctx, agent)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().filter(event => event.type === 'user/message'
         && event.data.source.kind === 'agent-instructions')).toHaveLength(2)
     } finally {
@@ -1821,6 +1846,7 @@ describe('workspace context request injection', () => {
       // The first resumed pre-step retains the compatible visible baseline and
       // appends only the offline file transition needed to reach current state.
       await write(join(root, 'AGENTS.md'), 'new root rule after offline edit')
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
 
       // Resume announces its lifecycle start before the first step.
@@ -1829,6 +1855,7 @@ describe('workspace context request injection', () => {
 
       const baselines = baselineEvents(resumed)
       expect(baselines).toHaveLength(1)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const latest = resumed.session.snapshotEvents().findLast(event =>
         event.type === 'user/message' && event.data.source.kind === 'agent-instructions')
       expect(latest?.type === 'user/message' ? latest.data.source : undefined).toMatchObject({
@@ -2010,6 +2037,7 @@ describe('workspace context request injection', () => {
 
       await composeBaselinePrefix(ctx, agent)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const contexts = agent.session.snapshotEvents().filter(event =>
         event.type === 'user/message' && event.data.source.kind !== 'user',
       )
@@ -2651,6 +2679,7 @@ describe('dynamic nested workspace context injection', () => {
 
       agent.followup(createUserMessage({ content: [{ type: 'text', text: 'read and abort' }], source: { kind: 'user' } }))
       await agent.whenIdle()
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().filter(event =>
         event.type === 'user/message' && event.data.source.kind !== 'user',
       )).toHaveLength(0)
@@ -2658,6 +2687,7 @@ describe('dynamic nested workspace context injection', () => {
       agent.followup(createUserMessage({ content: [{ type: 'text', text: 'retry the read' }], source: { kind: 'user' } }))
       await agent.whenIdle()
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const contexts = agent.session.snapshotEvents().filter(event => event.type === 'user/message' && event.data.source.kind !== 'user')
       expect(contexts).toHaveLength(1)
       expect(adapter.requests).toHaveLength(3)
@@ -3633,6 +3663,7 @@ describe('dynamic nested workspace context injection', () => {
         agent,
       })
       await appendAdditionalContexts(ctx, agent)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, agent.session.snapshotEvents())
 
       const afterResume = await ctx.tools.execute({
@@ -3667,10 +3698,12 @@ describe('dynamic nested workspace context injection', () => {
       })
       await appendAdditionalContexts(ctx, original)
       await write(join(root, 'pkg/AGENTS.md'), 'new nested rule after resume')
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
 
       await composeBaselinePrefix(ctx, resumed)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const update = resumed.session.snapshotEvents().findLast(event => event.type === 'user/message' && event.data.source.kind !== 'user')
       expect(update?.type === 'user/message' && update.data.source).toMatchObject({
         changes: [{ action: 'replace', scope: sk('pkg', 'AGENTS.md'), path: join('pkg', 'AGENTS.md') }],
@@ -4701,6 +4734,7 @@ describe('workspace context inbox synchronization', () => {
         callId: ToolCallId('recover-pending-a'), name: 'read', arguments: { file_path: join('a', 'file.txt') }, agent: original,
       })
       await syncWorkspaceContext(ctx, original)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumed = await stubAgent(root, original.session.snapshotEvents())
 
       await ctx.tools.execute({

+ 1 - 0
packages/context/session-reference/tests/loader-composition.spec.ts

@@ -134,6 +134,7 @@ describe('session-reference real Loader composition', () => {
     ctx.sessions.get(SessionId('reference-source'))!.append('user/message', createUserMessage({
       content: [{ type: 'text', text: 'LATER_SOURCE_MUTATION' }], source: { kind: 'user' },
     }), { surfaceOp: 'append' })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(Session.create(SessionId('replayed-target'), target.snapshotEvents()).deriveMessages()).toEqual(captured)
     expect(await readFile(notice.fullSnapshot.locator, 'utf8')).toBe(transcript)
   })

+ 4 - 0
packages/context/session-reference/tests/session-reference.spec.ts

@@ -322,6 +322,7 @@ describe('session reference spill outcomes', () => {
       const target = ctx.sessions.create(SessionId('target'))
       const source = ctx.sessions.create(SessionId('source'))
       for (const text of texts) appendText(source, text)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const captured = source.snapshotEvents().at(-1)?.seq
       const read = vi.spyOn(ctx.sessionQuery, 'readSurface')
       const store = ctx.spillStore as RecordingSpill
@@ -371,6 +372,7 @@ describe('session reference spill outcomes', () => {
       const context = result.additionalContext!
       target.append('user/message', context, { surfaceOp: 'append' })
       for (const detach of detachSources) detach()
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const replayed = Session.create(SessionId('replayed'), target.snapshotEvents()).deriveMessages()
       expect(replayed).toEqual(target.deriveMessages())
       expect(JSON.stringify(replayed)).toContain('memory:session-reference-1.txt')
@@ -451,6 +453,7 @@ describe('session reference spill outcomes', () => {
       finish.resolve(undefined)
       await rejected
       await settled.promise
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(target.snapshotEvents().filter(event => event.type === 'user/message')).toEqual([])
     } finally { finish.resolve(undefined); await ctx.fiber.dispose() }
   })
@@ -1198,6 +1201,7 @@ describe('session reference discovery and preparation', () => {
     expect(JSON.stringify(before)).toContain('durable referenced fact')
     expect(JSON.stringify(before)).toContain('use @source')
     expect(JSON.stringify(before)).not.toContain('later source mutation')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(Session.create(SessionId('replayed-target'), target.snapshotEvents()).deriveMessages()).toEqual(before)
   })
 

+ 1 - 0
packages/context/time-context/src/index.ts

@@ -80,6 +80,7 @@ function formatDuration(elapsedMs: number): string {
 function requestMessages(agent: Agent, turn: number, proposed: readonly UserMessage[]): UserMessage[] {
   const entered: UserMessage[] = []
   for (let seq = agent.session.seq - 1; seq >= 0; seq -= 1) {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = agent.session.eventAt(SessionSeq(seq))
     if (event?.type === 'turn/start' && event.data.turn === turn) {
       return [...entered.reverse(), ...proposed]

+ 2 - 0
packages/context/time-context/src/invariant.ts

@@ -161,6 +161,7 @@ function validateReading(
 /* jscpd:ignore-start -- package companions share replay and dispatch plumbing */
 /** Validate all package-owned readings already present in one session. */
 function validateSession(session: Session, fail: InvariantFailure): void {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   const events = session.snapshotEvents()
   for (const [index, event] of events.entries()) {
     if (event.type !== 'user/message'
@@ -180,6 +181,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant
     if (event.type !== 'user/message'
       || event.data.source.kind !== 'plugin'
       || event.data.source.plugin !== SOURCE_NAME) return
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     validateReading(session.snapshotEvents(), event, fail)
   }, { global: true })
 }, { inject: ['sessions'] })

+ 13 - 0
packages/context/time-context/tests/time-context.spec.ts

@@ -68,6 +68,7 @@ function openMessageTurn(session: Session, turn: number, clientTimeZone?: string
 
 function contextTexts(session: Session): string[] {
   const texts: string[] = []
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   for (const event of session.snapshotEvents()) {
     if (event.type === 'user/message'
       && event.data.source.kind === 'plugin'
@@ -168,6 +169,7 @@ describe('durable step context', () => {
       + 'Browser time zone for this request: Asia/Shanghai. Interpret otherwise-unqualified dates and times in this zone.\n'
       + 'Elapsed since the preceding model-visible message: 1d 1h 1m 1s.',
     ])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = session.snapshotEvents().at(-1)
     expect(event?.type).toBe('user/message')
     if (event?.type !== 'user/message') throw new Error('missing time context')
@@ -291,7 +293,9 @@ describe('durable step context', () => {
     const original = Session.create(SessionId('seed-source'))
     openMessageTurn(original, 1)
     await fire(ctx, sessionAgent(original), 1, 1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const user = original.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'user')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const reading = original.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'plugin')
     if (user === undefined || reading === undefined) throw new Error('missing source surface events')
     original.append('user/message', createUserMessage({
@@ -304,14 +308,17 @@ describe('durable step context', () => {
     original.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
     expect(JSON.stringify(original.deriveMessages())).not.toContain('Time sampled while preparing')
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const resumed = Session.create(SessionId('resumed'), original.snapshotEvents())
     const resumedAgent = sessionAgent(resumed)
     vi.setSystemTime(BASE + 999)
     openMessageTurn(resumed, 2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const beforeSkip = resumed.snapshotEvents().length
 
     await fire(ctx, resumedAgent, 2, 1)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(resumed.snapshotEvents()).toHaveLength(beforeSkip)
     expect(contextTexts(resumed)).toHaveLength(1)
 
@@ -334,6 +341,7 @@ describe('durable step context', () => {
 
     vi.setSystemTime(BASE + 500)
     openMessageTurn(first, 2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const beforeSkip = first.snapshotEvents().length
     await fire(ctx, firstAgent, 2, 1)
 
@@ -341,6 +349,7 @@ describe('durable step context', () => {
     openMessageTurn(independent, 1)
     await fire(ctx, sessionAgent(independent, 'independent-agent'), 1, 1)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(first.snapshotEvents()).toHaveLength(beforeSkip)
     expect(contextTexts(first)).toHaveLength(1)
     expect(contextTexts(independent)).toHaveLength(1)
@@ -460,6 +469,7 @@ describe('real agent-loop request history', () => {
 
     expect(contextTexts(agent.session)).toHaveLength(0)
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'step/start')).toBe(false)
     await ctx.fiber.dispose()
   })
@@ -482,8 +492,10 @@ describe('real agent-loop request history', () => {
     await agent.whenIdle()
 
     expect(adapter.requests).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const contexts = agent.session.snapshotEvents().filter(
       (event): event is SessionEvent<'user/message'> => event.type === 'user/message' && event.data.source.kind === 'plugin')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const starts = agent.session.snapshotEvents().filter(event => event.type === 'step/start')
     expect(contexts).toHaveLength(adapter.requests.length)
     expect(starts).toHaveLength(adapter.requests.length)
@@ -508,6 +520,7 @@ describe('real agent-loop request history', () => {
       expect(request.messages[0]?.role).toBe('system')
       expect(JSON.stringify(request.messages[0])).not.toContain('Time sampled while preparing')
     }
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const systemNodes = agent.session.snapshotEvents().filter(event => event.type === 'system/message')
     expect(JSON.stringify(systemNodes)).not.toContain('Time sampled while preparing')
     await ctx.fiber.dispose()

+ 2 - 0
packages/context/tmux-context/tests/tmux-context.spec.ts

@@ -123,6 +123,7 @@ function openMessageTurn(session: Session, turn: number): void {
 
 function contextTexts(session: Session): string[] {
   const texts: string[] = []
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   for (const event of session.snapshotEvents()) {
     if (event.type === 'user/message'
       && event.data.source.kind === 'plugin'
@@ -171,6 +172,7 @@ describe('tmux-context injection', () => {
       + 'window active=1, pane active=0, '
       + 'layout d517,270x71,0,0{135x71,0,0,87,134x71,136,0[134x35,136,0,90,134x35,136,36,93]}',
     ])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const event = session.snapshotEvents().at(-1)
     if (event?.type !== 'user/message') throw new Error('missing tmux context')
     // `snapshot` form: one named contribution carrying exactly the reading the

+ 1 - 0
packages/core/agent-loop/src/index.ts

@@ -748,6 +748,7 @@ export class AgentLoop extends Service implements AgentFactory {
    */
   private async appendUnstoredSuffix(stored: StoredSession | undefined, session: Session): Promise<void> {
     if (stored === undefined) return
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const suffix = session.snapshotEvents(SessionLogOffset(stored.storedCount))
     if (suffix.length > 0) await stored.handle.append(suffix)
     // Advance by what was stored, not to `session.seq`: an event appended

+ 1 - 0
packages/core/agent-loop/src/invariant.ts

@@ -28,6 +28,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant
       fail('a loop-built request must carry a frozen messages array')
     }
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = session.snapshotEvents()
     if (!events.some(event => event.type === 'step/start')) {
       return fail('a loop-built request with no step/start in its session log')

+ 2 - 0
packages/core/agent-loop/src/runtime-context.ts

@@ -45,6 +45,7 @@ export interface SystemPromptDecisionInput {
 
 /** Committed events from the newest backward; the restore scans stop at the first match. */
 function eventsNewestFirst(session: Session): readonly SessionEvent[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return session.snapshotEvents().toReversed()
 }
 
@@ -63,6 +64,7 @@ export class SystemPromptProjection {
   private systemNodes(): { seq: SessionSeq; text: string | undefined }[] {
     const nodes: { seq: SessionSeq; text: string | undefined }[] = []
     for (const seq of this.session.surface.nodes) {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const event = this.session.eventAt(seq)
       if (event?.type !== 'system/message') continue
       const content = event.data.message.content

+ 1 - 0
packages/core/agent-loop/tests/agent-initiator.spec.ts

@@ -335,6 +335,7 @@ describe('AgentLoop initiator scope', () => {
     }])
     const schema = adapter.requests[0]?.tools?.find(tool => tool.name === 'capability-request')
     expect(JSON.stringify(schema?.parameters)).not.toMatch(/session|harness/i)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const call = handle.agent.session.snapshotEvents().find(event => event.type === 'tool/call')
     expect(call?.type === 'tool/call' ? call.data.arguments : undefined)
       .toBe(JSON.stringify({ path: '/v1/capability' }))

+ 4 - 0
packages/core/agent-loop/tests/agent.spec.ts

@@ -35,6 +35,7 @@ describe('Agent', () => {
 
     agent.inject(createUserMessage({ content: [{ type: 'text', text: 'context' }], source: { kind: 'plugin', plugin: 'p' } }))
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().map(event => event.type)).toEqual(['agent/inbox/spliced'])
     expect(agent.status).toBe('idle')
     expect(adapter.requests).toHaveLength(0)
@@ -47,6 +48,7 @@ describe('Agent', () => {
 
     agent.inject(createUserMessage({ content: [{ type: 'text', text: 'empty plugin source' }], source: { kind: 'plugin', plugin: '' } }))
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const injected = agent.session.snapshotEvents().at(-1)
     expect(injected?.type === 'agent/inbox/spliced' && injected.data.inserted[0]?.source)
       .toEqual({ kind: 'plugin', plugin: '' })
@@ -97,6 +99,7 @@ describe('Agent', () => {
     expect(() => {
       agent.inject(createUserMessage({ content: [{ type: 'text', text: 'x', bad: 1n } as never], source: { kind: 'plugin', plugin: 'p' } }))
     }).toThrow(/non-JSON-serializable/)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()).toHaveLength(0)
   })
 
@@ -108,6 +111,7 @@ describe('Agent', () => {
     agent.steer(createUserMessage({ content: [{ type: 'text', text: 'steer idle' }], source: { kind: 'plugin', plugin: 'test' } }))
     await agent.whenIdle()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'user/message')).toBe(true)
     expect(adapter.requests).toHaveLength(1)
   })

+ 42 - 0
packages/core/agent-loop/tests/cancel.spec.ts

@@ -50,6 +50,7 @@ function waitForIdle(ctx: Context, agent: Agent): Promise<void> {
 
 /** All user-message texts recorded in the log (to assert what actually ran). */
 function userTexts(agent: Agent): string[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents()
     .filter(e => e.type === 'user/message')
     .flatMap(e => e.type === 'user/message' ? e.data.content : [])
@@ -71,6 +72,7 @@ describe('Agent.cancel()', () => {
 
     // The prompt ran: its user message is in the log and one turn completed.
     expect(userTexts(agent)).toEqual(['real prompt'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'turn/end')).toBe(true)
   })
 
@@ -86,12 +88,14 @@ describe('Agent.cancel()', () => {
     // A waking send starts and claims synchronously, so keepInbox has no
     // pending item to preserve by the time this cancellation runs.
     agent.cancel({ kind: 'user' }, { keepInbox: true })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event =>
       event.type === 'agent/inbox/spliced' && event.data.outcome === 'canceled')).toBe(false)
     await agent.whenIdle()
     expect(agent.inbox.nextTurn).toHaveLength(0)
     expect(userTexts(agent)).toEqual([])
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')?.data.reason)
       .toEqual({ kind: 'aborted', reason: { kind: 'user' } })
 
@@ -146,6 +150,7 @@ describe('Agent.cancel()', () => {
     expect(userTexts(agent)).toEqual(['active', 'B'])
     expect(adapter.requests).toHaveLength(2)
     expect(agent.inbox.nextTurn).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(e => e.type === 'turn/end').map(e =>
       e.type === 'turn/end' ? e.data.reason : null)).toEqual([
       { kind: 'aborted', reason: { kind: 'user' } },
@@ -196,6 +201,7 @@ describe('Agent.cancel()', () => {
     expect(agent.status).toBe('idle')
     // No replay with nothing to run: the latched message is gone, so no
     // empty follow-up turn is recorded.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(e => e.type === 'turn/start')).toHaveLength(1)
   })
 
@@ -255,8 +261,11 @@ describe('Agent.cancel()', () => {
     await new Promise(r => setTimeout(r, 30))
 
     expect(userTexts(agent)).toEqual([])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'step/start')).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')?.data.reason)
       .toEqual({ kind: 'aborted', reason: { kind: 'user' } })
     expect(agent.status).toBe('idle')
@@ -286,6 +295,7 @@ describe('Agent.cancel()', () => {
     await driverDone(agent)
 
     expect(agent.status).toBe('idle')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(false)
     expect(userTexts(agent)).toEqual([])
     expect(adapter.requests).toHaveLength(0)
@@ -323,6 +333,7 @@ describe('Agent.cancel()', () => {
       replacementObservation = agent.whenIdle().then(() => ({
         status: agent.status,
         requests: adapter.requests.length,
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         turns: agent.session.snapshotEvents().filter(event => event.type === 'turn/start').length,
       }))
       agent.cancel({ kind: 'user' })
@@ -400,6 +411,7 @@ describe('Agent.cancel()', () => {
 
     expect(reasons).toEqual([{ kind: 'aborted', reason: { kind: 'user' } }])
     expect(userTexts(agent)).toEqual(['go'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
     expect(adapter.requests).toHaveLength(1)
   })
@@ -436,7 +448,9 @@ describe('Agent.cancel()', () => {
 
     expect(executions).toBe(0)
     expect(reasons).toEqual([{ kind: 'aborted', reason: { kind: 'user' } }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const call = agent.session.snapshotEvents().find(event => event.type === 'tool/call')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const result = agent.session.snapshotEvents().find(event => event.type === 'tool/result')
     expect(call?.type === 'tool/call' ? call.data.callId : undefined).toBe('c1')
     expect(result?.type === 'tool/result' ? result.data : undefined).toMatchObject({
@@ -477,6 +491,7 @@ describe('Agent.cancel()', () => {
 
     expect(userTexts(agent)).toContain('second')
     // The second turn completed (its reply was streamed).
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const reasons = agent.session.snapshotEvents().filter(e => e.type === 'turn/end')
     expect(reasons.length).toBe(2)
   })
@@ -493,6 +508,7 @@ describe('Agent.cancel()', () => {
 
     // The prefix the user watched stream is committed as the step's message,
     // carrying the truncation marker and exact embedded stream that delivered it.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const message = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')
     expect(message?.type === 'assistant/message' ? message.data.message.content : undefined)
       .toEqual([{ type: 'text', text: 'partial' }])
@@ -503,6 +519,7 @@ describe('Agent.cancel()', () => {
       ))
       : false).toBe(true)
     expect(message?.sourceEventSeqs).toBeUndefined()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = agent.session.snapshotEvents().map(e => e.type)
     expect(types.indexOf('assistant/message')).toBeLessThan(types.indexOf('step/end'))
     expect(types.indexOf('step/end')).toBeLessThan(types.indexOf('turn/end'))
@@ -537,12 +554,14 @@ describe('Agent.cancel()', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const message = agent.session.snapshotEvents().find(event => event.type === 'assistant/message')
     expect(message?.type === 'assistant/message' ? message.data.message.source.replayState : undefined)
       .toEqual(replayState)
     expect(message?.type === 'assistant/message' ? message.data.interrupted : undefined).toBe(true)
     expect(() => Session.fromRestore(
       agent.session.id,
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       structuredClone([...agent.session.snapshotEvents()]),
       structuredClone(agent.session.header),
       SessionLogOffset(0),
@@ -566,6 +585,7 @@ describe('Agent.cancel()', () => {
     agent.cancel({ kind: 'user' })
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const message = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')
     expect(message?.type === 'assistant/message' ? message.data.message.content : undefined)
       .toEqual([{ type: 'reasoning', text: 'thinking about it' }])
@@ -593,9 +613,11 @@ describe('Agent.cancel()', () => {
     await waitForIdle(ctx, agent)
 
     // The undispatched call is dropped whole — no dangling tool_use to pair.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const message = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')
     expect(message?.type === 'assistant/message' ? message.data.message.content : undefined)
       .toEqual([{ type: 'text', text: 'reading the file' }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'tool/call')).toBe(false)
   })
 
@@ -618,7 +640,9 @@ describe('Agent.cancel()', () => {
 
     // The failed stream's prefix stays off the surface: clients reset it on
     // retry, and provider failures commit nothing.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'assistant/message')).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().find(e => e.type === 'turn/end')
     expect(end?.type === 'turn/end' ? end.data.reason.kind : undefined).toBe('aborted')
   })
@@ -639,12 +663,14 @@ describe('Agent.cancel()', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const messages = agent.session.snapshotEvents().filter(e => e.type === 'assistant/message')
     expect(messages).toHaveLength(1)
     const message = messages[0]!
     expect(message.type === 'assistant/message' ? message.data.message.content : undefined)
       .toEqual([{ type: 'text', text: 'recovered' }])
     expect(message.type === 'assistant/message' ? message.data.interrupted : undefined).toBeUndefined()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const failed = agent.session.snapshotEvents().find(e => e.type === 'assistant/attempt')
     expect(failed?.type === 'assistant/attempt'
       ? expandAssistantStream(failed.data.stream).some(member => (
@@ -676,10 +702,12 @@ describe('Agent.cancel()', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const attempt = agent.session.snapshotEvents().find(event => event.type === 'assistant/attempt')
     expect(attempt?.type === 'assistant/attempt'
       ? expandAssistantStream(attempt.data.stream).map(member => member.chunk)
       : []).toContainEqual({ type: 'text-delta', index: 0, text: 'partial before failure' })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().find(event => event.type === 'turn/end')).toMatchObject({
       type: 'turn/end', data: { reason: { kind: 'error', error: { message: failure.message } } },
     })
@@ -700,6 +728,7 @@ describe('Agent.cancel()', () => {
     agent.cancel({ kind: 'user' })
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'assistant/message')).toBe(false)
   })
 
@@ -729,6 +758,7 @@ describe('Agent.cancel()', () => {
     // log is balanced (the open step was closed by the cancel branch).
     expect(streamed).toBe(false)
     expect(reasons).toEqual([{ kind: 'aborted', reason: { kind: 'user' } }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = agent.session.snapshotEvents().map(e => e.type)
     expect(types.filter(t => t === 'step/start').length).toBe(types.filter(t => t === 'step/end').length)
   })
@@ -764,7 +794,9 @@ describe('Agent.cancel()', () => {
 
     expect(streamed).toBe(false)
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'turn/end')).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = agent.session.snapshotEvents().map(e => e.type)
     expect(types.filter(t => t === 'step/start').length).toBe(types.filter(t => t === 'step/end').length)
   })
@@ -818,6 +850,7 @@ describe('Agent.cancel()', () => {
     // No turn opened, no step streamed, and a later prompt still runs (the marker
     // was reset).
     expect(streamed).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'turn/start')).toBe(false)
   })
 
@@ -849,6 +882,7 @@ describe('Agent.cancel()', () => {
     await replacementIdle
     expect(userTexts(agent)).toEqual(['B', 'C'])
     expect(adapter.requests).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/end')).toHaveLength(2)
   })
 
@@ -872,6 +906,7 @@ describe('Agent.cancel()', () => {
     await replacementIdle
     expect(userTexts(agent)).toEqual(['B', 'C'])
     expect(adapter.requests).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/end')).toHaveLength(3)
   })
 
@@ -893,9 +928,11 @@ describe('Agent.cancel()', () => {
     // started from the dropped steering.
     await new Promise(r => setTimeout(r, 30))
     expect(agent.status).toBe('idle')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnStarts = agent.session.snapshotEvents().filter(e => e.type === 'turn/start')
     expect(turnStarts.length).toBe(1) // only the original (cancelled) turn
     // The steering text was dropped — it never reached the log.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const flat = agent.session.snapshotEvents()
       .filter(e => e.type === 'user/message')
       .flatMap(e => e.data.content)
@@ -927,6 +964,7 @@ describe('Agent.cancel()', () => {
             status: agent.status,
             requests: adapter.requests.length,
             users: userTexts(agent),
+            // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
             events: agent.session.snapshotEvents().map(event => event.type),
           })}`))
         }, 1000)
@@ -938,6 +976,7 @@ describe('Agent.cancel()', () => {
     expect(adapter.requests).toHaveLength(2)
     expect(userTexts(agent)).toEqual(['original', 'replacement'])
     expect(agent.inbox.nextTurn).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const reasons = agent.session.snapshotEvents()
       .filter(event => event.type === 'turn/end')
       .map(event => event.type === 'turn/end' ? event.data.reason : undefined)
@@ -965,6 +1004,7 @@ describe('Agent.cancel()', () => {
     const runtimeReason: unknown = adapter.requests[0]?.signal?.reason
     expect(runtimeReason).toEqual({ kind: 'parent' })
     expect(runtimeReason).toBe(supplied)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason).toEqual({
       kind: 'aborted',
@@ -986,6 +1026,7 @@ describe('Agent.cancel()', () => {
     agent.cancel({ kind: 'user' })
     await handle.dispose()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason).toEqual({ kind: 'aborted', reason: { kind: 'user' } })
   })
@@ -1057,6 +1098,7 @@ describe('Agent.cancel()', () => {
     const idle = agent.whenIdle()
     agent.cancel({ kind: 'user' })
     await idle
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason)
       .toEqual({ kind: 'aborted', reason: { kind: 'user' } })

+ 40 - 0
packages/core/agent-loop/tests/contract-regressions.spec.ts

@@ -72,6 +72,7 @@ describe('assistant replay provider and model fields', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const recorded = agent.session.snapshotEvents().find(event => event.type === 'assistant/message')
     expect(recorded?.type === 'assistant/message' && recorded.data.message.source).toEqual({
       kind: 'model', provider: 'mock', model: 'next-model', replayState,
@@ -111,6 +112,7 @@ describe('abort during tool execution ends the turn', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .filter(event => event.type === 'tool/result'
         || (event.type === 'user/message' && event.data.source.kind === 'plugin')
@@ -124,6 +126,7 @@ describe('abort during tool execution ends the turn', () => {
     send(agent, 'wake')
     await idle
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .flatMap(event => event.type === 'user/message' && event.data.source.kind === 'plugin'
         ? [event.data.content]
@@ -174,6 +177,7 @@ describe('abort during tool execution ends the turn', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents()
     expect(events
       .filter(event => event.type === 'tool/result'
@@ -199,9 +203,11 @@ describe('abort during tool execution ends the turn', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start'
       || event.type === 'step/start' || event.type === 'turn/end').map(event => event.type))
       .toEqual(['turn/start', 'turn/end'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().find(event => event.type === 'turn/end')?.data)
       .toEqual({ turn: 1, reason: { kind: 'completed' } })
     expect(agent.inbox.nextTurn).toHaveLength(0)
@@ -243,19 +249,23 @@ describe('abort during tool execution ends the turn', () => {
     await started.promise
     await fiber.dispose()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .flatMap(event => event.type === 'user/message' && event.data.source.kind === 'plugin'
         ? [event.data.content]
         : []))
       .toEqual([])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .flatMap(event => event.type === 'agent/inbox/spliced' && event.data.target === 'next-step'
         ? [event.data.inserted.map(inboxText)]
         : [])
       .at(-1))
       .toEqual(['accepted result context during disposal'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start'))
       .toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().find(event => event.type === 'turn/end')?.data.reason)
       .toEqual({ kind: 'aborted', reason: { kind: 'disposed' } })
   })
@@ -310,6 +320,7 @@ describe('abort during tool execution ends the turn', () => {
     send(agent, 'start a text-only turn')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'user/message' && event.data.source.kind === 'plugin'
         ? [event.data.content]
@@ -362,6 +373,7 @@ describe('plugin exceptions are contained', () => {
 
     send(agent, 'first')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')).toMatchObject({
       data: { reason: { kind: 'error', error: { message: 'broken continuation plugin', code: 'UNKNOWN' } } },
     })
@@ -398,7 +410,9 @@ describe('disposal leaves the two-state status contract balanced', () => {
 
     expect(statuses).toEqual(['running', 'idle'])
     expect(reasons).toEqual([{ kind: 'aborted', reason: { kind: 'disposed' } }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const messages = agent.session.snapshotEvents()
       .filter(event => event.type === 'user/message')
       .flatMap(event => event.data.content)
@@ -448,6 +462,7 @@ describe('adapter registration, routing, and accepted-input ownership', () => {
 
     send(agent, 'go')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason.kind === 'error'
       ? turnEnd.data.reason.error.message
@@ -510,6 +525,7 @@ describe('adapter registration, routing, and accepted-input ownership', () => {
       ['content', 'id', 'role', 'source'],
     ])
     expect(targets).toEqual(['next-turn', 'next-step'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const steeringSources = agent.session.snapshotEvents().flatMap(e =>
       e.type === 'user/message' && e.data.source.kind === 'plugin' ? [e.data.source] : [])
     expect(steeringSources).toEqual([{ kind: 'plugin', plugin: 'goal' }])
@@ -543,6 +559,7 @@ describe('adapter registration, routing, and accepted-input ownership', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents()
     const claims = events.flatMap(event => event.type === 'agent/inbox/spliced'
       && event.data.target === 'next-step'
@@ -588,6 +605,7 @@ describe('turn numbering continues across seeded sessions', () => {
 
     const { agent: forked } = await ctx2.agents.create({
       sessionId: SessionId('forked'),
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: agent.session.snapshotEvents(),
       agentOptions: { provider: 'mock', model: 'mock' },
     })
@@ -656,6 +674,7 @@ describe('a finish-error stream chunk ends the turn as error, not completed', ()
     expect(errors[0]).toBeInstanceOf(LlmError)
     expect((errors[0] as LlmError).failure).toEqual(failure)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents()
     const turnEnd = events.find(event => event.type === 'turn/end')
     expect(turnEnd).toMatchObject({ data: { reason: { kind: 'error', error: failure } } })
@@ -678,6 +697,7 @@ describe('a finish-error stream chunk ends the turn as error, not completed', ()
     await waitForIdle(ctx, agent)
 
     expect(reasons).toEqual([{ kind: 'error', error: { message: 'model stream aborted', code: 'ABORTED' } }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'assistant/message')).toBe(false)
   })
 
@@ -708,6 +728,7 @@ describe('step boundary publication order', () => {
     const observed: { turn: number; step: number; lastEventType: string | undefined; sawStepStart: boolean }[] = []
     ctx.on('session/event', (subject, event) => {
       if (subject !== agent.session || event.type !== 'step/start') return
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const events = subject.snapshotEvents()
       const last = events.at(-1)
       observed.push({
@@ -744,6 +765,7 @@ describe('turn and step boundary recovery', () => {
 
   /** Count turn/step boundary events for balance assertions. */
   function boundaryCounts(agent: Agent) {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     return {
       turnStart: e.filter(x => x.type === 'turn/start').length,
@@ -774,6 +796,7 @@ describe('turn and step boundary recovery', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     const c = boundaryCounts(agent)
     expect(c).toMatchObject({ turnStart: 1, turnEnd: 1, stepStart: 1, stepEnd: 1, errors: 0 })
@@ -807,6 +830,7 @@ describe('turn and step boundary recovery', () => {
     send(agent, 'rejected')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'turn/start'
       || event.type === 'user/message')).toBe(false)
     expect(agent.inbox.nextTurn).toHaveLength(1)
@@ -838,6 +862,7 @@ describe('turn and step boundary recovery', () => {
       stepEnd: 0,
       errors: 1,
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')).toMatchObject({
       data: { reason: { kind: 'error', error: { message: 'reject step-start before commit', code: 'UNKNOWN' } } },
     })
@@ -931,6 +956,7 @@ describe('turn and step boundary recovery', () => {
     await fiber.dispose() // dispose during the hanging step
     await driverDone(agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     const turnStarts = e.filter(x => x.type === 'turn/start').length
     const turnEnds = e.filter(x => x.type === 'turn/end').length
@@ -964,6 +990,7 @@ describe('turn and step boundary recovery', () => {
     send(agent, 'go')
     await agent.whenIdle()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     expect(e.filter(x => x.type === 'turn/start' || x.type === 'turn/end').map(x => x.type))
       .toEqual(['turn/start', 'turn/end'])
@@ -993,11 +1020,14 @@ describe('turn and step boundary recovery', () => {
     expect(errors).toEqual([])
     // Session contains the observer failure per listener, so the committed turn
     // remains visible to later observers and executes normally.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = agent.session.snapshotEvents().map(e => e.type)
     expect(types.filter(t => t === 'turn/start')).toHaveLength(1)
     expect(types.filter(t => t === 'turn/end')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const lastBoundary = agent.session.snapshotEvents().findLast(e => e.type === 'turn/start' || e.type === 'turn/end')
     expect(lastBoundary?.type).toBe('turn/end')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().at(-1)?.type).toBe('turn/end')
 
     // loop survives: a second turn runs normally.
@@ -1030,6 +1060,7 @@ describe('turn and step boundary recovery', () => {
       .toEqual({ kind: 'completed' })
 
     // step/end precedes turn/end (ordering contract)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     const stepEndIdx = e.findIndex(x => x.type === 'step/end')
     const turnEndIdx = e.findIndex(x => x.type === 'turn/end')
@@ -1064,6 +1095,7 @@ describe('turn and step boundary recovery', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     // Both step/end and turn/end are present — finalization ran to completion.
     expect(e.some(x => x.type === 'step/end')).toBe(true)
@@ -1094,6 +1126,7 @@ describe('turn and step boundary recovery', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
     // turn 1 is balanced despite the throwing turn/end listener.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e1 = agent.session.snapshotEvents()
     expect(e1.filter(x => x.type === 'turn/start')).toHaveLength(1)
     expect(e1.filter(x => x.type === 'turn/end')).toHaveLength(1)
@@ -1103,6 +1136,7 @@ describe('turn and step boundary recovery', () => {
     send(agent, 'again')
     await waitForIdle(ctx, agent)
     expect(adapter.requests).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(x => x.type === 'turn/end')).toHaveLength(2)
   })
 })
@@ -1135,6 +1169,7 @@ describe('tool result call identity', () => {
     await waitForIdle(ctx, agent)
 
     // The logged tool/result.callId is the originating call.id.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const resultEvent = agent.session.snapshotEvents().find(e => e.type === 'tool/result')
     expect(resultEvent?.type).toBe('tool/result')
     if (resultEvent?.type === 'tool/result') {
@@ -1199,6 +1234,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
     await driverDone(agent)
     unlisten()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     expect(e.filter(x => x.type === 'turn/start' || x.type === 'turn/end').map(x => x.type))
       .toEqual(['turn/start', 'turn/end'])
@@ -1247,6 +1283,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
     await driverDone(agent)
     unlisten()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     expect(e.filter(x => x.type === 'turn/start' || x.type === 'turn/end').map(x => x.type))
       .toEqual(['turn/start', 'turn/end'])
@@ -1297,6 +1334,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
     await driverDone(agent)
 
     // The post-listener cancellation check catches disposal before any step or LLM call.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     expect(e.filter(x => x.type === 'turn/start' || x.type === 'turn/end').map(x => x.type))
       .toEqual(['turn/start', 'turn/end'])
@@ -1344,6 +1382,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
     await fiber.dispose()
     await driverDone(agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     expect(e.filter(x => x.type === 'turn/start' || x.type === 'turn/end').map(x => x.type))
       .toEqual(['turn/start', 'turn/end'])
@@ -1389,6 +1428,7 @@ describe('disposal and cancellation during pre-step assembly', () => {
     await disposalDone
     await driverDone(agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const e = agent.session.snapshotEvents()
     expect(e.filter(x => x.type === 'turn/start' || x.type === 'turn/end').map(x => x.type))
       .toEqual(['turn/start', 'turn/end'])

+ 18 - 0
packages/core/agent-loop/tests/coverage-edges.spec.ts

@@ -69,12 +69,14 @@ describe('tool JSON parse', () => {
     await waitForIdle(ctx, agent)
 
     // tool/call event should have recorded the raw arguments string
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const callEvent = agent.session.snapshotEvents().find(e => e.type === 'tool/call')
     expect(callEvent).toBeDefined()
     if (callEvent!.type === 'tool/call') {
       expect(callEvent!.data.arguments).toBe('not json')
     }
     // the loop did not crash — a result was produced
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'tool/result')).toBe(true)
   })
 
@@ -101,6 +103,7 @@ describe('tool JSON parse', () => {
     send(agent, 'use tool')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'tool/result')).toBe(true)
   })
 })
@@ -130,8 +133,11 @@ describe('thrown-value propagation', () => {
     expect(errors).toHaveLength(1)
     expect(errors[0]).toBe('naked string error')
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const starts = agent.session.snapshotEvents().filter(event => event.type === 'turn/start')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const ends = agent.session.snapshotEvents().filter(event => event.type === 'turn/end')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const messages = agent.session.snapshotEvents().filter(event => event.type === 'user/message')
     expect(starts).toHaveLength(0)
     expect(ends).toHaveLength(0)
@@ -155,6 +161,7 @@ describe('thrown-value propagation', () => {
 
     send(agent, 'go')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().find(e => e.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason.kind === 'error'
       ? turnEnd.data.reason.error.message
@@ -180,6 +187,7 @@ describe('durable error rendering', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().find(e => e.type === 'turn/end')
     expect(turnEnd).toBeDefined()
     if (turnEnd?.type === 'turn/end' && turnEnd.data.reason.kind === 'error') {
@@ -236,6 +244,7 @@ describe('structured tool error propagation (the runtime-validation Agent Note,
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const toolResult = agent.session.snapshotEvents().find(e => e.type === 'tool/result')
     expect(toolResult?.type === 'tool/result' && toolResult.data.message.content[0].isError).toBe(true)
     expect(toolResult?.type === 'tool/result' && toolResult.data.error)
@@ -262,6 +271,7 @@ describe('request-error action edges', () => {
 
     // One failed request, no retry turn.
     expect(adapter.requests).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const ends = agent.session.snapshotEvents().filter(e => e.type === 'turn/end')
     expect(ends).toHaveLength(1)
   })
@@ -284,6 +294,7 @@ describe('request-error action edges', () => {
     await agent.whenIdle()
 
     expect(adapter.requests).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(end?.type === 'turn/end' && end.data.reason.kind).toBe('aborted')
   })
@@ -313,6 +324,7 @@ describe('stream failure edges', () => {
 
     // No facts -> not offered to recovery; the turn fails through settle().
     expect(recoveries).toBe(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(end?.type === 'turn/end' && end.data.reason.kind).toBe('error')
   })
@@ -388,6 +400,7 @@ describe('tool result meta persistence', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const result = agent.session.snapshotEvents().find(e => e.type === 'tool/result')
     expect(result?.type === 'tool/result' && result.data.meta).toEqual({ presentation: 'diff-card' })
   })
@@ -436,6 +449,7 @@ describe('recovery without a retry action', () => {
 
     expect(recoveries).toBe(1)
     expect(adapter.requests).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(end?.type === 'turn/end' && end.data.reason.kind).toBe('error')
   })
@@ -461,6 +475,7 @@ describe('unrenderable failure settlement', () => {
     send(agent, 'go')
     await agent.whenIdle()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(end?.type === 'turn/end' && end.data.reason.kind).toBe('error')
     if (end?.type === 'turn/end' && end.data.reason.kind === 'error') {
@@ -502,6 +517,7 @@ describe('driver bookkeeping edges', () => {
 
     expect(proposals).toBe(2)
     expect(adapter.requests).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')
     expect(end?.type === 'turn/end' && end.data.reason).toEqual({ kind: 'blocked' })
   })
@@ -524,8 +540,10 @@ describe('driver bookkeeping edges', () => {
     send(agent, 'go')
     await agent.whenIdle()
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = agent.session.snapshotEvents().map(e => e.type)
     expect(types.filter(t => t === 'step/end')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const end = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(end?.type === 'turn/end' && end.data.reason.kind).toBe('error')
   })

+ 4 - 0
packages/core/agent-loop/tests/inbox.spec.ts

@@ -142,6 +142,7 @@ describe('ReactLoopInbox', () => {
     const childAgent = stubAgent('inbox-fork-child', { ctx, session: child })
     const childInbox = new ReactLoopInbox(ctx.sessionProjections, child, agentEvents(ctx, childAgent))
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child.inheritedEventCount).toBe(parent.snapshotEvents().length)
     expect(childInbox.nextTurn).toEqual([inherited])
 
@@ -246,6 +247,7 @@ describe('ReactLoopInbox', () => {
     const nextStep = createUserMessage({ content: [{ type: 'text', text: 'step' }], source: { kind: 'user' } })
     agent.inbox.append('next-turn', nextTurn)
     agent.inbox.append('next-step', nextStep)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const beforeClear = session.snapshotEvents().length
 
     agent.inbox.clear()
@@ -253,6 +255,7 @@ describe('ReactLoopInbox', () => {
     expect(agent.inbox.nextTurn).toEqual([])
     expect(agent.inbox.nextStep).toEqual([])
     expect(discarded).toEqual([nextStep, nextTurn])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents().slice(beforeClear).map(event => event.type === 'agent/inbox/spliced'
       ? event.data
       : event.type)).toEqual([
@@ -261,6 +264,7 @@ describe('ReactLoopInbox', () => {
     ])
 
     agent.inbox.clear()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents()).toHaveLength(beforeClear + 2)
   })
 })

+ 1 - 0
packages/core/agent-loop/tests/interception.spec.ts

@@ -57,6 +57,7 @@ function send(agent: Agent, text: string) {
 }
 
 function events(agent: Agent): readonly SessionEvent[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents()
 }
 

+ 69 - 0
packages/core/agent-loop/tests/loop.spec.ts

@@ -53,6 +53,7 @@ function systemOf(request: GenerateOptions | undefined): string | undefined {
 
 /** All user-message texts recorded in the log (to assert what actually ran). */
 function userTexts(agent: Agent): string[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents()
     .filter(e => e.type === 'user/message')
     .flatMap(e => e.type === 'user/message' ? e.data.content : [])
@@ -72,6 +73,7 @@ describe('agent loop', () => {
       if (subject !== agent) return
       frames.push(frame)
       if (frame.type === 'end' && frame.outcome.kind === 'committed') {
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         committedAfterMessage = agent.session.snapshotEvents().at(-1)?.type === 'assistant/message'
       }
     })
@@ -91,7 +93,9 @@ describe('agent loop', () => {
       (frame): frame is Extract<AssistantStreamFrame, { type: 'chunk' }> => frame.type === 'chunk',
     )
     expect(chunks.map(frame => frame.index)).toEqual(chunks.map((_frame, index) => index))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => (event.type as string) === 'assistant/chunk')).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const message = agent.session.snapshotEvents().findLast(event => event.type === 'assistant/message')
     expect(message?.type === 'assistant/message'
       ? expandAssistantStream(message.data.stream).map(member => member.chunk)
@@ -133,6 +137,7 @@ describe('agent loop', () => {
       type: 'end',
       outcome: { kind: 'abandoned' },
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'assistant/message')).toBe(false)
   })
 
@@ -160,9 +165,11 @@ describe('agent loop', () => {
       index: 2,
       outcome: { kind: 'abandoned' },
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => (
       event.type === 'assistant/message' || event.type === 'assistant/attempt'
     ))).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().at(-1)).toMatchObject({
       type: 'turn/end',
       data: { reason: { kind: 'error' } },
@@ -218,6 +225,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(frames).toEqual([])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().at(-1)).toMatchObject({
       type: 'turn/end',
       data: { reason: { kind: 'error' } },
@@ -433,6 +441,7 @@ describe('agent loop', () => {
 
     expect(userTexts(agent)).toEqual([])
     expect(adapter.requests).toEqual([])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(e => e.type === 'turn/start')).toHaveLength(0)
   })
 
@@ -456,12 +465,14 @@ describe('agent loop', () => {
 
     expect(order).toEqual(['turn/start', 'step/start', 'step/end', 'turn/end'])
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = agent.session.snapshotEvents().map(e => e.type)
     // Durable inbox receipt precedes the turn-owned transcript.
     expect(types[0]).toBe('agent/inbox/spliced')
     expect(types).toContain('turn/start')
     expect(types).toContain('user/message')
     expect(types).toContain('assistant/message')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const assistantMessage = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')
     expect(assistantMessage?.type === 'assistant/message' && assistantMessage.data.usage).toEqual({ inputTokens: 10, outputTokens: 'hello there'.length })
     expect(types.at(-1)).toBe('turn/end')
@@ -504,6 +515,7 @@ describe('agent loop', () => {
     expect((block).content).toEqual([{ type: 'text', text: 'echo: ping' }])
 
     // session log records call + result
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = agent.session.snapshotEvents().map(e => e.type)
     expect(types).toContain('tool/call')
     expect(types).toContain('tool/result')
@@ -566,6 +578,7 @@ describe('agent loop', () => {
     expect(errors.map(error => error.message)).toEqual([
       'prompt variable "{{cwd}}" has no value for this assembly (section "deployment:persona-prefix")',
     ])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().find(e => e.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason.kind).toBe('error')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason.kind === 'error'
@@ -583,6 +596,7 @@ describe('agent loop', () => {
 
     expect(adapter.requests).toHaveLength(1)
     expect(systemOf(adapter.requests[0])).toBe('You are an AI agent powered by DeepSeek Harness.\n\nIn /rescued.')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnds = agent.session.snapshotEvents().filter(e => e.type === 'turn/end')
     expect(turnEnds).toHaveLength(2)
     expect(turnEnds[1]?.type === 'turn/end' && turnEnds[1].data.reason.kind).toBe('completed')
@@ -630,6 +644,7 @@ describe('agent loop', () => {
     expect(adapter.requests).toHaveLength(1)
     expect('system' in adapter.requests[0]!).toBe(false)
     expect(adapter.requests[0]!.messages.map(message => message.role)).toEqual(['user'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'system/message')).toMatchObject([
       { data: { message: { role: 'system', content: [] } }, surfaceOp: 'append' },
     ])
@@ -644,6 +659,7 @@ describe('agent loop', () => {
       const firstIdle = waitForIdle(ctx, agent)
       send(agent, 'first')
       await firstIdle
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const head = agent.session.snapshotEvents().find(event => event.type === 'system/message')
       expect(adapter.requests[0]?.messages.map(message => message.role)).toEqual(['user'])
       expect(head).toMatchObject({ data: { message: { content: [] } }, surfaceOp: 'append' })
@@ -656,12 +672,14 @@ describe('agent loop', () => {
       expect(adapter.requests).toHaveLength(2)
       expect(systemOf(adapter.requests[1])).toBe('You are an AI agent powered by DeepSeek Harness.')
       expect(adapter.requests[1]?.messages.map(message => message.role)).toEqual(['system', 'user', 'assistant', 'user'])
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const replacement = agent.session.snapshotEvents().findLast(event => event.type === 'system/message')
       expect(replacement).toMatchObject({
         surfaceOp: { op: 'replace', startSeq: head?.seq, endSeq: head?.seq },
         sourceEventSeqs: [head?.seq],
       })
       expect(agent.session.surface.nodes[0]).toBe(replacement?.seq)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().flatMap(event =>
         event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series'])
     } finally {
@@ -681,6 +699,7 @@ describe('agent loop', () => {
     let mode = 'read-only'
     const dispose = ctx.systemPrompt.context({ name: 'policy', order: 0, text: () => `Mode: ${mode}.` })
     const agent = await ctx.agentLoop.create(SessionId('a-runtime-context'), { provider: 'mock', model: 'mock' })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const contextEvents = () => agent.session.snapshotEvents().flatMap(event =>
       event.type === 'user/message'
         && event.data.source.kind === 'plugin'
@@ -722,7 +741,9 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
     expect(contextEvents()).toHaveLength(3)
     expect(adapter.requests.map(systemOf)).toEqual(Array(5).fill(systemOf(adapter.requests[0])))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'system/message')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial'])
   })
@@ -735,6 +756,7 @@ describe('agent loop', () => {
 
     send(agent, 'first')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const contextEvent = agent.session.snapshotEvents().find(event =>
       event.type === 'user/message'
       && event.data.source.kind === 'plugin'
@@ -750,6 +772,7 @@ describe('agent loop', () => {
 
     send(agent, 'after compaction')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const runtimeContexts = agent.session.snapshotEvents().flatMap(event =>
       event.type === 'user/message'
         && event.data.source.kind === 'plugin'
@@ -770,6 +793,7 @@ describe('agent loop', () => {
 
     send(agent, 'first')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const contextEvent = agent.session.snapshotEvents().find(event =>
       event.type === 'user/message'
       && event.data.source.kind === 'plugin'
@@ -832,6 +856,7 @@ describe('agent loop', () => {
 
     send(agent, 'repair context')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const runtimeContexts = agent.session.snapshotEvents().flatMap(event =>
       event.type === 'user/message'
         && event.data.source.kind === 'plugin'
@@ -853,6 +878,7 @@ describe('agent loop', () => {
     send(agent, 'hi')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const message = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')
     const chunks = message?.type === 'assistant/message' ? expandAssistantStream(message.data.stream) : []
     // textResponse('abc') = block-start + 3 deltas + block-end + usage + finish = 7
@@ -887,12 +913,14 @@ describe('agent loop', () => {
     send(agent, 'start')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const steering = agent.session.snapshotEvents().find(e =>
       e.type === 'user/message' && JSON.stringify(e.data.content).includes('change of plans'))
     expect(steering).toBeDefined()
     // The entered batch is appended after the second step opens and before its
     // request derives history.
     const steeringSeq = steering!.seq
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const secondStepStart = agent.session.snapshotEvents().filter(e => e.type === 'step/start')[1]
     expect(secondStepStart).toBeDefined()
     expect(steeringSeq).toBeGreaterThan(secondStepStart!.seq)
@@ -911,11 +939,14 @@ describe('agent loop', () => {
     const idle = waitForIdle(ctx, agent)
     agent.steer(createUserMessage({ content: [{ type: 'text', text: 'first idle steer' }], source: { kind: 'user' } }))
     expect(agent.status).toBe('running')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
     agent.steer(createUserMessage({ content: [{ type: 'text', text: 'second idle steer' }], source: { kind: 'user' } }))
     await idle
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .filter(event => event.type === 'user/message')
       .map(event => event.data.content)).toEqual([
@@ -944,7 +975,9 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/end')).toHaveLength(1)
     expect(agent.inbox.nextStep).toHaveLength(1)
 
@@ -952,6 +985,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(adapter.requests).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(2)
     expect(JSON.stringify(adapter.requests[0]?.messages)).toContain('pending steering')
   })
@@ -964,7 +998,9 @@ describe('agent loop', () => {
     agent.inject(createUserMessage({ content: [{ type: 'text', text: 'file changed: a.ts' }], source: { kind: 'plugin', plugin: 'watcher' } }))
     expect(agent.status).toBe('idle')
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().at(-1)).toMatchObject({
       type: 'agent/inbox/spliced',
       data: {
@@ -979,6 +1015,7 @@ describe('agent loop', () => {
 
     send(agent, 'go')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
     const flat = JSON.stringify(adapter.requests[0]!.messages)
     expect(flat).toContain('file changed: a.ts')
@@ -994,6 +1031,7 @@ describe('agent loop', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const contextEvent = agent.session.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'plugin')
     expect(contextEvent?.type === 'user/message' && contextEvent.data.source)
       .toEqual({ kind: 'plugin', plugin: 'agent-instructions' })
@@ -1020,6 +1058,7 @@ describe('agent loop', () => {
         agent.inject(createUserMessage({ content: [first], source: { kind: 'plugin', plugin: 'x' } }))
         first.text = 'mutated after inject'
         agent.inject(createUserMessage({ content: [{ type: 'text', text: 'second notice' }], source: { kind: 'plugin', plugin: 'x' } }))
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         visibleDuringTool = agent.session.snapshotEvents().some(e => e.type === 'user/message' && e.data.source.kind === 'plugin')
         return [{ type: 'text', text: 'ok' }]
       },
@@ -1032,9 +1071,12 @@ describe('agent loop', () => {
 
     // The injection stays in the open turn, but its user-role context cannot
     // split the assistant tool call from the provider's tool-result message.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnStarts = agent.session.snapshotEvents().filter(e => e.type === 'turn/start')
     expect(turnStarts).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const result = agent.session.snapshotEvents().find(e => e.type === 'tool/result')!
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const contexts = agent.session.snapshotEvents().filter(e => e.type === 'user/message' && e.data.source.kind === 'plugin')
     expect(contexts).toHaveLength(2)
     expect(result.seq).toBeLessThan(contexts[0]!.seq)
@@ -1079,6 +1121,7 @@ describe('agent loop', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'user/message' && event.data.source.kind === 'plugin')).toBe(false)
   })
 
@@ -1124,6 +1167,7 @@ describe('agent loop', () => {
     // only one model call despite the tool call requesting a follow-up
     expect(adapter.requests).toHaveLength(1)
     // The tool still executes and durably records its result.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'tool/result')).toBe(true)
   })
 
@@ -1150,6 +1194,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(adapter.requests).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents().map(event => event.type)
     expect(events.filter(type => type === 'turn/end')).toHaveLength(1)
     expect(JSON.stringify(adapter.requests[1]?.messages)).toContain('late steering')
@@ -1179,6 +1224,7 @@ describe('agent loop', () => {
     expect(adapter.requests[0]!.model).toBe('other-model')
     // The header event records what the request ACTUALLY used — the switch is
     // a reconstructable fact, not silent drift.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const headerEvent = agent.session.snapshotEvents().find(e => e.type === 'request/header')
     expect(headerEvent?.type === 'request/header' && headerEvent.data.header.config.model).toBe('other-model')
   })
@@ -1218,6 +1264,7 @@ describe('agent loop', () => {
 
     let boundaryOpen = true
     ctx.on('agent/pre-step', ({ agent: subject }, next) => {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       if (subject === agent) boundaryOpen = subject.session.snapshotEvents().at(-1)?.type === 'step/start'
       return next()
     })
@@ -1250,13 +1297,16 @@ describe('agent loop', () => {
     // The first proposal failed inside a balanced turn without calling the model.
     expect(errors.map(error => error.message)).toEqual(['boom in pre-step'])
     expect(adapter.requests.length).toBe(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(event => event.type === 'turn/end')).toBe(true)
 
     // The loop survived: a second prompt runs a normal completed turn.
     send(agent, 'second')
     await waitForIdle(ctx, agent)
     expect(adapter.requests.length).toBe(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const lastTurnEnd = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(lastTurnEnd?.type === 'turn/end' && lastTurnEnd.data.reason).toEqual({ kind: 'completed' })
   })
@@ -1303,6 +1353,7 @@ describe('agent loop', () => {
     expect(adapter.requests).toHaveLength(1)
     expect(reasons).toEqual([{ kind: 'max-tokens' }])
     // Assert the durable row, not only the live listener.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(turnEnd!.data.reason).toEqual({ kind: 'max-tokens' })
   })
@@ -1407,6 +1458,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(executions).toBe(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'tool/call')).toBe(false)
     expect(agent.session.deriveMessages().slice(1)).toEqual([{
       id: expect.any(String) as unknown,
@@ -1417,6 +1469,7 @@ describe('agent loop', () => {
     expect(reasons).toEqual([{ kind: 'max-tokens' }])
     // Empty content still needs an assistant/message to carry usage; derivation
     // skips that host so it does not create a spurious assistant turn.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const assistantMessage = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')
     expect(assistantMessage?.type === 'assistant/message' && assistantMessage.data).toMatchObject({
       turn: 1,
@@ -1457,6 +1510,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(reasons).toEqual([{ kind: 'max-tokens' }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const assistant = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')!
     expect(assistant.type === 'assistant/message' && assistant.data).toMatchObject({
       turn: 1,
@@ -1492,6 +1546,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(reasons).toEqual([{ kind: 'completed' }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const assistant = agent.session.snapshotEvents().find(e => e.type === 'assistant/message')!
     expect(assistant.type === 'assistant/message' && assistant.data).toMatchObject({
       turn: 1,
@@ -1535,6 +1590,7 @@ describe('agent loop', () => {
     send(agent, 'continue')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'tool/call')).toBe(false)
     // The follow-up request replays the truncated message with its replay
     // metadata pruned in step with the dropped tool call.
@@ -1603,6 +1659,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(adapter.requests).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().findLast(e => e.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason.kind).toBe('completed')
   })
@@ -1624,7 +1681,9 @@ describe('agent loop', () => {
     send(agent, 'outer message')
     await idle
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turns = agent.session.snapshotEvents().filter(event => event.type === 'turn/start')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const messages = agent.session.snapshotEvents()
       .filter(event => event.type === 'user/message')
       .map(event => event.data.content)
@@ -1643,7 +1702,9 @@ describe('agent loop', () => {
     agent.followup(createUserMessage({ content: [{ type: 'text', text: 'plugin message' }], source: { kind: 'plugin', plugin: 'test' } }))
     await idle
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turns = agent.session.snapshotEvents().filter(event => event.type === 'turn/start')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const sources = agent.session.snapshotEvents()
       .filter(event => event.type === 'user/message')
       .map(event => event.data.source)
@@ -1699,9 +1760,11 @@ describe('agent loop', () => {
     send(agent, 'outer message')
     await idle
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const messages = agent.session.snapshotEvents()
       .filter(event => event.type === 'user/message')
       .map(event => event.data.content)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(2)
     expect(messages).toEqual([
       [{ type: 'text', text: 'outer message' }],
@@ -1732,6 +1795,7 @@ describe('agent loop', () => {
     })
     expect(reasons[0]).toMatchObject({ kind: 'error' })
     // The durable failure and live relay describe the same failed turn.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().find(e => e.type === 'turn/end')
     expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason).toMatchObject({ kind: 'error' })
   })
@@ -1785,6 +1849,7 @@ describe('agent loop', () => {
     await waitForIdle(ctx, agent)
     expect(adapter.requests).toHaveLength(1)
     expect(adapter.requests[0]?.reasoningEffort).toBe(effort)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const header = agent.session.snapshotEvents().find(event => event.type === 'request/header')
     expect(header?.type === 'request/header' && header.data.header.config.reasoningEffort).toBe(effort)
   })
@@ -1823,11 +1888,15 @@ describe('agent loop', () => {
     send(agent, 'run')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const replayed = ctx.sessions.create(SessionId('replayed'), { seed: [...agent.session.snapshotEvents()] })
     expect(replayed.deriveMessages()).toEqual(agent.session.deriveMessages())
     // event-by-event identity of types over the inherited prefix
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(replayed.snapshotEvents().slice(0, agent.session.seq).map(e => e.type)).toEqual(
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       agent.session.snapshotEvents().map(e => e.type))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(replayed.snapshotEvents().at(-1)?.type).toBe('session/end-seed')
   })
 })

+ 4 - 0
packages/core/agent-loop/tests/properties.spec.ts

@@ -72,18 +72,21 @@ function recordStatus(ctx: Context, agent: Agent): { seen: string[]; dispose: ()
 }
 
 function userMessageTexts(agent: Agent): string[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents()
     .filter(e => e.type === 'user/message')
     .map(e => (e.data as { content: { type: string; text?: string }[] }).content.map(b => b.text ?? '').join(''))
 }
 
 function turnNumbers(agent: Agent): number[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents()
     .filter(e => e.type === 'turn/start')
     .map(e => e.data.turn)
 }
 
 function turnEndNumbers(agent: Agent): number[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents()
     .filter(e => e.type === 'turn/end')
     .map(e => (e.data as { turn: number }).turn)
@@ -91,6 +94,7 @@ function turnEndNumbers(agent: Agent): number[] {
 
 function userMessageCountsByTurn(agent: Agent): number[] {
   const counts: number[] = []
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   for (const event of agent.session.snapshotEvents()) {
     if (event.type === 'turn/start') counts.push(0)
     if (event.type === 'user/message') counts[counts.length - 1]! += 1

+ 1 - 0
packages/core/agent-loop/tests/request-cache.e2e.ts

@@ -82,6 +82,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('log-derived request cache hits (
     agent.followup(createUserMessage({ content: [{ type: 'text', text: 'Thanks. Repeat that value one more time.' }], source: { kind: 'user' } }))
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const usages = agent.session.snapshotEvents()
       .filter(e => e.type === 'assistant/message')
       .map(e => e.data.usage)

+ 6 - 0
packages/core/agent-loop/tests/request-error.spec.ts

@@ -92,12 +92,14 @@ describe('agent/request-error', () => {
         code: 'SERVICE_UNAVAILABLE',
       },
     ])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
     expect(seen.map(item => item.retryPolicy)).toEqual([
       expect.objectContaining({ mode: 'normal' }),
       expect.objectContaining({ mode: 'normal' }),
     ])
     expect(statuses).toEqual(['running', 'idle'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial'])
   })
@@ -115,7 +117,9 @@ describe('agent/request-error', () => {
     await agent.whenIdle()
 
     expect(adapter.requests).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().find(event => event.type === 'turn/end')).toMatchObject({
       type: 'turn/end',
       data: { reason: { kind: 'aborted', reason: { kind: 'user' } } },
@@ -137,7 +141,9 @@ describe('agent/request-error', () => {
     await agent.whenIdle()
 
     expect(adapter.requests).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'turn/start')).toHaveLength(1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().find(event => event.type === 'turn/end')).toMatchObject({
       type: 'turn/end',
       data: { reason: { kind: 'error' } },

+ 3 - 0
packages/core/agent-loop/tests/request-freeze.spec.ts

@@ -58,6 +58,7 @@ describe('loop-owned request freezing', () => {
       }),
       stream: [],
     }, { surfaceOp: 'append' })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = structuredClone(seed.snapshotEvents())
     const userEvent = events.find(event => event.type === 'user/message')!
     const assistantEvent = events.find(event => event.type === 'assistant/message')!
@@ -198,6 +199,7 @@ describe('loop-owned request freezing', () => {
       expect(() => request.stop!.push('mutate')).toThrow(TypeError)
       if (!registered) expect(request.stop).toBe(stops[index])
     }
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(event => event.type === 'request/header')).toHaveLength(1)
     expect(agent.session.requestHeader()!.tools).not.toBe(requests[0]!.tools)
     expect(agent.session.requestHeader()!.config.stop).not.toBe(requests[0]!.stop)
@@ -221,6 +223,7 @@ describe('loop-owned request freezing', () => {
       await agent.whenIdle()
       expect(request.signal!.aborted).toBe(true)
       expect(request.signal!.reason).toEqual({ kind: 'user' })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().at(-1)).toMatchObject({
         type: 'turn/end', data: { reason: { kind: 'aborted', reason: { kind: 'user' } } },
       })

+ 41 - 0
packages/core/agent-loop/tests/request-reconstruction.spec.ts

@@ -94,6 +94,7 @@ describe('request stability across the loop', () => {
       expect(Object.isFrozen(request.messages)).toBe(true)
     }
     // One anchoring header snapshot; no further header events (nothing changed).
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const headerEvents = agent.session.snapshotEvents().filter(e => e.type === 'request/header')
     expect(headerEvents).toHaveLength(1)
     expect(headerEvents[0]?.type === 'request/header' && headerEvents[0].data.reason).toBe('initial')
@@ -111,6 +112,7 @@ describe('request stability across the loop', () => {
 
     expect(adapter.requests).toHaveLength(2)
     expectPrefixExtension(adapter.requests[0]!, adapter.requests[1]!)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial'])
   })
@@ -132,6 +134,7 @@ describe('request stability across the loop', () => {
     await waitForIdle(ctx, agent)
 
     expectPrefixExtension(adapter.requests[0]!, adapter.requests[1]!)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series'])
   })
@@ -157,6 +160,7 @@ describe('request stability across the loop', () => {
     await waitForIdle(ctx, agent)
 
     expectPrefixExtension(adapter.requests[0]!, adapter.requests[1]!)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event => event.type === 'request/header'
       ? [{ reason: event.data.reason, startsSeries: event.data.startsSeries }]
       : [])).toEqual([
@@ -194,6 +198,7 @@ describe('request stability across the loop', () => {
     await waitForIdle(ctx, agent)
 
     expectPrefixExtension(adapter.requests[0]!, adapter.requests[1]!)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series'])
   })
@@ -223,6 +228,7 @@ describe('request stability across the loop', () => {
       ReasoningEffortId('high'),
       ReasoningEffortId('max'),
     ])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const headers = agent.session.snapshotEvents().filter(event => event.type === 'request/header')
     expect(headers.map(event => event.data.header.config.reasoningEffort)).toEqual([
       ReasoningEffortId('high'),
@@ -242,6 +248,7 @@ describe('request stability across the loop', () => {
       const resumedCtx = await harness(resumedAdapter)
       const resumedHandle = await resumedCtx.agents.create({
         sessionId: SessionId(`effort-${model}`),
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         seed: structuredClone(agent.session.snapshotEvents()),
         agentOptions: { provider: 'mock', model },
       })
@@ -250,6 +257,7 @@ describe('request stability across the loop', () => {
 
       expect(resumedAdapter.requests[0]?.model).toBe(model)
       expect(resumedAdapter.requests[0]?.reasoningEffort).toBe(effort)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const resumedHeaders = resumedHandle.agent.session.snapshotEvents().filter(event => event.type === 'request/header')
       expect(resumedHeaders.at(-1)?.data.header.config.reasoningEffort).toBe(effort)
       expect(resumedHeaders.at(-1)?.data.reason).toBe('resume')
@@ -268,6 +276,7 @@ describe('request stability across the loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(adapter.requests[0]?.maxTokens).toBe(256_000)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const header = agent.session.snapshotEvents().find(event => event.type === 'request/header')
     expect(header?.type === 'request/header' && header.data.header.config.maxTokens).toBe(256_000)
     expect(header?.type === 'request/header' && header.data.header.adapterDefaults)
@@ -299,6 +308,7 @@ describe('request stability across the loop', () => {
 
     expect(deepseek.requests[0]?.maxTokens).toBe(256_000)
     expect(other.requests[0]?.maxTokens).toBe(8_192)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const headers = agent.session.snapshotEvents().filter(event => event.type === 'request/header')
     expect(headers.map(event => event.data.header.config.maxTokens)).toEqual([256_000, 8_192])
     expect(headers.map(event => event.data.header.adapterDefaults)).toEqual([
@@ -333,6 +343,7 @@ describe('request stability across the loop', () => {
 
     expect(deepseek.requests[0]?.maxTokens).toBe(4_096)
     expect(other.requests[0]?.maxTokens).toBe(4_096)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const headers = agent.session.snapshotEvents().filter(event => event.type === 'request/header')
     expect(headers.map(event => event.data.header.config.maxTokens)).toEqual([4_096, 4_096])
     expect(headers.map(event => event.data.header.adapterDefaults)).toEqual([undefined, undefined])
@@ -385,6 +396,7 @@ describe('request stability across the loop', () => {
       ReasoningEffortId('high'),
     ])
     expect(second.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const headers = agent.session.snapshotEvents().filter(event => event.type === 'request/header')
     expect(headers.at(-1)?.data.header.config.reasoningEffort).toBe(ReasoningEffortId('high'))
   })
@@ -423,6 +435,7 @@ describe('request stability across the loop', () => {
     expect(signal.aborted).toBe(true)
     expect(handle.agent.status).toBe('idle')
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(handle.agent.session.snapshotEvents().some(event => event.type === 'request/header')).toBe(false)
   })
 
@@ -446,6 +459,7 @@ describe('request stability across the loop', () => {
       send(agent, 'go')
       await waitForIdle(ctx, agent)
 
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')).toMatchObject({
         data: {
           reason: failure instanceof LlmError
@@ -521,6 +535,7 @@ describe('request stability across the loop', () => {
     // The rewritten history: summary replaces turn 1's user+assistant pair behind the system prompt.
     expect(second.messages[0]!.role).toBe('system')
     expect(second.messages[1]!.content.some(b => b.type === 'text' && b.text.includes('[summary of turn 1]'))).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event => event.type === 'request/header'
       ? [{ reason: event.data.reason, startsSeries: event.data.startsSeries }]
       : [])).toEqual([
@@ -559,6 +574,7 @@ describe('request stability across the loop', () => {
     expect(adapter.requests[1]?.messages[1]?.content).toContainEqual({
       type: 'text', text: '[summary for retry]',
     })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series'])
   })
@@ -572,6 +588,7 @@ describe('request stability across the loop', () => {
     await waitForIdle(ctx, agent)
     send(agent, 'second')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial'])
 
@@ -579,8 +596,10 @@ describe('request stability across the loop', () => {
     send(agent, 'third')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const snapshots = agent.session.snapshotEvents().filter(e => e.type === 'request/header')
     expect(snapshots.map(event => event.data.reason)).toEqual(['initial', 'series'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const systemNodes = agent.session.snapshotEvents().filter(e => e.type === 'system/message')
     expect(systemNodes).toHaveLength(2)
     expect(systemNodes[1]?.surfaceOp).toEqual({ op: 'replace', startSeq: systemNodes[0]?.seq, endSeq: systemNodes[0]?.seq })
@@ -610,8 +629,10 @@ describe('request stability across the loop', () => {
     await waitForIdle(ctx, agent)
 
     // No new series: the header stays, node 0 stays, and the prompt update follows the cached prefix.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const systemNodes = agent.session.snapshotEvents().filter(e => e.type === 'system/message')
     expect(systemNodes).toHaveLength(2)
     expect(systemNodes[1]?.surfaceOp).toBe('append')
@@ -625,6 +646,7 @@ describe('request stability across the loop', () => {
     // An unchanged prompt adds nothing on the next step.
     send(agent, 'fourth')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().filter(e => e.type === 'system/message')).toHaveLength(2)
     expectPrefixExtension(adapter.requests[2]!, adapter.requests[3]!)
   })
@@ -648,9 +670,11 @@ describe('request stability across the loop', () => {
     startSeries = true
     send(agent, 'second')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     let systemNodes = agent.session.snapshotEvents().filter(e => e.type === 'system/message')
     expect(systemNodes).toHaveLength(2)
     expect(systemNodes[1]?.surfaceOp).toEqual({ op: 'replace', startSeq: systemNodes[0]?.seq, endSeq: systemNodes[0]?.seq })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series'])
 
@@ -660,6 +684,7 @@ describe('request stability across the loop', () => {
     disposeSection = ctx.systemPrompt.section({ name: 'extra', order: 2, text: 'newer guidance' })
     send(agent, 'third')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     systemNodes = agent.session.snapshotEvents().filter(e => e.type === 'system/message')
     expect(systemNodes).toHaveLength(3)
     expect(systemNodes[2]?.surfaceOp).toBe('append')
@@ -670,6 +695,7 @@ describe('request stability across the loop', () => {
     ctx.systemPrompt.section({ name: 'extra', order: 2, text: 'newest guidance' })
     send(agent, 'fourth')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     systemNodes = agent.session.snapshotEvents().filter(e => e.type === 'system/message')
     expect(systemNodes).toHaveLength(5)
     expect(systemNodes[3]?.data.message.content).toEqual([])
@@ -703,10 +729,12 @@ describe('request stability across the loop', () => {
     send(agent, 'second')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const systemNodes = agent.session.snapshotEvents().filter(e => e.type === 'system/message')
     expect(systemNodes).toHaveLength(2)
     expect(systemNodes[1]?.surfaceOp).toEqual({ op: 'replace', startSeq: systemNodes[0]?.seq, endSeq: systemNodes[0]?.seq })
     expect(adapter.requests[1]!.messages.map(message => message.role)).toEqual(['system', 'user', 'user'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series'])
   })
@@ -725,8 +753,10 @@ describe('request stability across the loop', () => {
     send(agent, 'second')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const headers = agent.session.snapshotEvents().filter(e => e.type === 'request/header')
     expect(headers.map(event => [event.data.reason, event.data.startsSeries])).toEqual([['initial', undefined], ['change', true]])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const systemNodes = agent.session.snapshotEvents().filter(e => e.type === 'system/message')
     expect(systemNodes).toHaveLength(2)
     expect(systemNodes[1]?.surfaceOp).toEqual({ op: 'replace', startSeq: systemNodes[0]?.seq, endSeq: systemNodes[0]?.seq })
@@ -752,6 +782,7 @@ describe('request stability across the loop', () => {
     const first = adapter.requests[0]!
     // The inject landed in the log after the boundary: not in THIS request…
     expect(first.messages.some(m => m.content.some(b => b.type === 'text' && b.text.includes('[late context]')))).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'user/message' && e.data.source.kind === 'plugin')).toBe(true)
 
     send(agent, 'second')
@@ -779,6 +810,7 @@ describe('request stability across the loop', () => {
     send(agent, 'go')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnEnd = agent.session.snapshotEvents().findLast(event => event.type === 'turn/end')
     expect(turnEnd).toMatchObject({ data: { reason: { kind: 'error' } } })
     if (turnEnd?.type !== 'turn/end' || turnEnd.data.reason.kind !== 'error') throw new Error()
@@ -798,6 +830,7 @@ describe('request stability across the loop', () => {
     const ctx2 = await harness(adapter2)
     const handle = await ctx2.agents.create({
       sessionId: SessionId('gen2-session'),
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: agent.session.snapshotEvents(),
       agentOptions: { provider: 'mock', model: 'mock' },
     })
@@ -805,11 +838,13 @@ describe('request stability across the loop', () => {
     send(agent2, 'second')
     await waitForIdle(ctx2, agent2)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const snapshots = agent2.session.snapshotEvents().filter(e => e.type === 'request/header')
     expect(snapshots).toHaveLength(2)
     expect(snapshots[1]?.data.reason).toBe('resume')
     // Identical header and an unchanged system node across the restart: byte-identical continuation.
     expect(adapter2.requests[0]!.messages[0]).toEqual(adapter.requests[0]!.messages[0])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent2.session.snapshotEvents().filter(event => event.type === 'system/message')).toHaveLength(1)
     expectPrefixExtension(adapter.requests[0]!, adapter2.requests[0]!)
   })
@@ -838,6 +873,7 @@ describe('request stability across the loop', () => {
 
     // The second turn reuses the same series and header; the session's own
     // fold remains immutable state.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().flatMap(event =>
       event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial'])
     expect(Object.isFrozen(agent.session.requestHeader())).toBe(true)
@@ -864,6 +900,7 @@ describe('request stability across the loop', () => {
     await waitForIdle(ctx, agent)
 
     expect(adapter.requests).toHaveLength(3)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = agent.session.snapshotEvents()
     const stepStarts = events.filter(e => e.type === 'step/start')
     expect(stepStarts).toHaveLength(3)
@@ -920,6 +957,7 @@ describe('request/context capacity records', () => {
     send(agent, 'second')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const records = agent.session.snapshotEvents().filter(event => event.type === 'request/context')
     expect(records).toHaveLength(1)
     expect(records[0]?.data).toEqual({ provider: 'mock', model: 'mock', contextWindow: 128_000 })
@@ -945,6 +983,7 @@ describe('request/context capacity records', () => {
     send(agent, 'second')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .filter(event => event.type === 'request/context')
       .map(event => event.data.contextWindow)).toEqual([64_000, 256_000])
@@ -957,6 +996,7 @@ describe('request/context capacity records', () => {
     await waitForIdle(ctx, agent)
     send(agent, 'second')
     await waitForIdle(ctx, agent)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .filter(event => event.type === 'request/context')
       .map(event => event.data)).toEqual([{ provider: 'mock', model: 'mock' }])
@@ -977,6 +1017,7 @@ describe('request/context capacity records', () => {
     send(agent, 'second')
     await waitForIdle(ctx, agent)
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents()
       .filter(event => event.type === 'request/context')
       .map(event => event.data)).toEqual([

+ 9 - 0
packages/core/agent-loop/tests/resume.spec.ts

@@ -484,6 +484,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
     // Lifecycle 2: resume repairs the tail and stores the repair durably.
     const ctx2 = await mountPersistentHarness(root, new MockAdapter([]))
     const handle = await ctx2.agents.resume({ resumeSessionId: sessionId })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(handle.agent.session.snapshotEvents().map(event => event.type))
       .toEqual(['turn/start', 'turn/end', 'session/end-seed'])
     await handle.dispose()
@@ -552,6 +553,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
     // closers immediately after the committed prefix — no gap, no fragment.
     const ctx2 = await mountPersistentHarness(root, new MockAdapter([]), 'none')
     const handle = await ctx2.agents.resume({ resumeSessionId: sessionId })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(handle.agent.session.snapshotEvents().map(event => event.type))
       .toEqual(['turn/start', 'turn/end', 'session/end-seed'])
     await handle.dispose()
@@ -639,6 +641,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
       setup: async (agentCtx, agent) => {
         expect(agent.id).toBe(sessionId)
         // The two persisted events plus the end-seed marker.
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         expect(agent.session.snapshotEvents()).toHaveLength(3)
         agentCtx.on('session/created', () => void order.push('setup-listener:session/created'))
         agentCtx.on('agent/created', () => void order.push('setup-listener:agent/created'))
@@ -691,6 +694,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
     expect(stored.map(event => event.type)).toEqual(['turn/start', 'turn/end', 'session/end-seed'])
 
     const second = await ctx.agents.resume({ resumeSessionId: sessionId })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(second.agent.session.snapshotEvents().map(event => event.type))
       .toEqual(['turn/start', 'turn/end', 'session/end-seed'])
     await second.dispose()
@@ -983,6 +987,7 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
     const a1 = h1.agent
     a1.followup(createUserMessage({ content: [{ type: 'text', text: 'first question' }], source: { kind: 'user' } }))
     await waitForIdle(ctx1, a1)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events1 = a1.session.snapshotEvents()
     const seqs1 = events1.map(e => e.seq)
     expect(seqs1).toEqual([...seqs1].sort((x, y) => x - y)) // contiguous
@@ -996,8 +1001,10 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
     // The resumed session carries the prior history…
     expect(a2.session.id).toBe('sess-resume')
     // …followed by one end-seed event marking the constructor seed.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(a2.session.snapshotEvents().length).toBe(events1.length + 1)
     expect(a2.session.firstLiveSeq).toBe(events1.length)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(a2.session.snapshotEvents().at(-1)?.type).toBe('session/end-seed')
     const replay = Session.create(SessionId('replay'), events1)
     expect(a2.session.deriveMessages()).toEqual(replay.deriveMessages())
@@ -1005,8 +1012,10 @@ describe('the session-persistence Agent Note: AgentLoop factory create/resume',
     // …and a new turn continues numbering (turn 2) with contiguous seqs.
     a2.followup(createUserMessage({ content: [{ type: 'text', text: 'second question' }], source: { kind: 'user' } }))
     await waitForIdle(ctx2, a2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const allSeqs = a2.session.snapshotEvents().map(e => e.seq)
     expect(allSeqs).toEqual(allSeqs.map((_, i) => i)) // 0..N contiguous, no duplicates
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const turnStarts = a2.session.snapshotEvents().filter(e => e.type === 'turn/start')
     expect(turnStarts.map(e => e.type === 'turn/start' && e.data.turn)).toEqual([1, 2])
     await ctx2.fiber.dispose()

+ 3 - 0
packages/core/agent-loop/tests/scope-lifecycle.spec.ts

@@ -809,6 +809,7 @@ describe('agent scope lifecycle', () => {
     expect(statuses).toEqual([])
     expect(observerSawLive).toBe(true)
     expect(scopeDisposed).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(announced.session.snapshotEvents()).toEqual([])
     expect(ctx.agents.get(SessionId('session-start-dispose-s'))).toBeUndefined()
     expect(ctx.sessions.get(SessionId('session-start-dispose-s'))).toBeUndefined()
@@ -1114,8 +1115,10 @@ describe('agent scope lifecycle', () => {
     // are empty and nothing still drives the detached session.
     expect(ctx.agents.get(agent.id)).toBeUndefined()
     expect(ctx.sessions.get(agent.id)).toBeUndefined()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const eventsAfter = agent.session.snapshotEvents().length
     await new Promise(resolve => setTimeout(resolve, 30))
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().length).toBe(eventsAfter)
     await ctx.fiber.dispose()
   })

+ 20 - 0
packages/core/agent-loop/tests/system-prompt-admission.spec.ts

@@ -39,6 +39,7 @@ async function harness(capable = new MockAdapter(Array.from({ length: 8 }, () =>
   ctx.on('llm/stream', (request, next) => {
     // Rebuild from copied source events, not the live projection's cached state.
     const subject = ctx.agents.get(request.sessionId!)!
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const replay = Session.create(subject.id, subject.session.snapshotEvents())
     expect(request.messages).toEqual(replay.deriveMessages())
     expect(request.system).toBeUndefined()
@@ -83,21 +84,26 @@ describe('prepared-route prompt admission', () => {
     expect(systemTexts(cleared)).toEqual([])
     expect(toPiContext(cleared).systemPrompt).toBeUndefined()
     expect(JSON.stringify(toPiContext(cleared))).not.toContain('prompt ')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const clearEvents = h.agent.session.snapshotEvents().filter(event => event.type === 'system/message').filter(event => event.data.turn === 4)
     expect(clearEvents).toHaveLength(3)
     for (const event of clearEvents) {
       expect(event.data.message.content).toEqual([])
       expect(event.surfaceOp).toEqual({ op: 'replace', startSeq: event.sourceEventSeqs?.[0], endSeq: event.sourceEventSeqs?.[0] })
     }
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const count = h.agent.session.snapshotEvents().filter(event => event.type === 'system/message').length
     await send(h.agent, 'still clear')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(h.agent.session.snapshotEvents().filter(event => event.type === 'system/message')).toHaveLength(count)
     expect(systemTexts(adapter.requests.at(-1)!)).toEqual([])
     const { agent: resumed } = await h.ctx.agents.create({
       sessionId: SessionId('cleared-resume'), agentOptions: { provider, model: 'model' },
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: [...h.agent.session.snapshotEvents()],
     })
     await send(resumed, 'resume clear')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(resumed.session.snapshotEvents().filter(event => event.type === 'system/message')).toHaveLength(count)
     expect(systemTexts(adapter.requests.at(-1)!)).toEqual([])
     h.setPrompt('restored instruction')
@@ -124,6 +130,7 @@ describe('prepared-route prompt admission', () => {
     }
     await send(h.agent, 'third')
     expectPlain(h.capable.requests[2]!, 'prompt two')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const header = h.agent.session.snapshotEvents().filter(event => event.type === 'request/header').at(-1)
     expect(header?.data.reason).toBe(reason === 'explicit' ? 'series' : 'change')
     if (reason === 'tools') expect(header?.data.startsSeries).toBe(true)
@@ -147,6 +154,7 @@ describe('prepared-route prompt admission', () => {
       expect(failure.code).toBe('CONTEXT_LENGTH')
       if (attempts++ === 0) {
         const nodes = h.agent.session.surface.nodes
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         const latest = nodes.findLast(seq => h.agent.session.eventAt(seq)?.type === 'system/message')!
         const start = retainOlder ? latest : nodes[1]!
         const replaced = nodes.slice(nodes.indexOf(start), nodes.indexOf(latest) + 1)
@@ -166,6 +174,7 @@ describe('prepared-route prompt admission', () => {
     expectPlain(adapter.requests[3]!, 'prompt three')
     expect(adapter.requests[4]!.messages).toEqual(adapter.requests[3]!.messages)
     expect(adapter.requests[3]!.messages.at(-1)?.content).toEqual([{ type: 'text', text: 'third' }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = h.agent.session.snapshotEvents()
     expect(events.filter(event => event.type === 'step/start')).toHaveLength(3)
     expect(events.filter(event => event.type === 'user/message' && event.data.source.kind === 'user')).toHaveLength(3)
@@ -183,6 +192,7 @@ describe('prepared-route prompt admission', () => {
     if (changed) h.setPrompt('prompt three')
     await send(h.agent, 'third')
     expectPlain(h.plain.requests[0]!, changed ? 'prompt three' : 'prompt two')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = h.agent.session.snapshotEvents()
     const replacements = events.filter(event => event.type === 'system/message').filter(event => event.surfaceOp !== 'append')
     expect(replacements).toHaveLength(2)
@@ -191,6 +201,7 @@ describe('prepared-route prompt admission', () => {
       expect(event.surfaceOp).toEqual({ op: 'replace', startSeq: event.sourceEventSeqs?.[0], endSeq: event.sourceEventSeqs?.[0] })
     }
     await send(h.agent, 'fourth')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(h.agent.session.snapshotEvents().filter(event => event.type === 'system/message')).toHaveLength(4)
     expectPlain(h.plain.requests[1]!, changed ? 'prompt three' : 'prompt two')
   })
@@ -214,6 +225,7 @@ describe('prepared-route prompt admission', () => {
     expect(messages.slice(-2).map(message => message.content)).toEqual([
       [{ type: 'text', text: 'second' }], notice,
     ])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const notices = h.agent.session.snapshotEvents().filter(event => event.type === 'user/message'
       && event.data.source.kind === 'plugin' && event.data.source.plugin === 'model-selection')
     expect(notices).toHaveLength(1)
@@ -230,10 +242,12 @@ describe('prepared-route prompt admission', () => {
     await send(h.agent, 'second')
     const { agent: resumed } = await h.ctx.agents.create({
       sessionId: SessionId('resumed-capable'), agentOptions: { provider: 'capable', model: 'model' },
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       seed: [...h.agent.session.snapshotEvents()],
     })
     h.ctx.on('agent/pre-step', ({ agent }, next) => {
       if (agent === resumed && replace) {
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         const seq = agent.session.surface.nodes.find(seq => agent.session.eventAt(seq)?.type === 'user/message')!
         agent.session.append('user/message', createUserMessage({
           content: [{ type: 'text', text: 'compacted history' }], source: { kind: 'plugin', plugin: 'test-compaction' },
@@ -245,6 +259,7 @@ describe('prepared-route prompt admission', () => {
     expect(systemTexts(h.capable.requests[2]!)).toEqual(replace
       ? [[{ type: 'text', text: 'prompt two' }]]
       : [[{ type: 'text', text: 'prompt one' }], [{ type: 'text', text: 'prompt two' }]])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(resumed.session.snapshotEvents().filter(event => event.type === 'request/header').map(event => event.data.reason))
       .toEqual(['initial', 'resume'])
   })
@@ -254,10 +269,12 @@ describe('prepared-route prompt admission', () => {
     await send(h.agent, 'first')
     h.setPrompt('prompt two')
     await send(h.agent, 'second')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const seed = h.agent.session.snapshotEvents()
     const { agent: resumed } = await h.ctx.agents.create({ sessionId: SessionId('resumed'), agentOptions: { provider: 'plain', model: 'model' }, seed: [...seed] })
     await send(resumed, 'resume')
     expectPlain(h.plain.requests[0]!, 'prompt two')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(resumed.session.snapshotEvents().filter(event => event.type === 'request/header').at(-1)?.data.reason).toBe('resume')
   })
 
@@ -266,6 +283,7 @@ describe('prepared-route prompt admission', () => {
     const entered = Promise.withResolvers<undefined>()
     const release = Promise.withResolvers<undefined>()
     const observe = async () => {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const types = h.agent.session.snapshotEvents().map(event => event.type)
       expect(types).toContain('step/start')
       expect(types).not.toContain('system/message')
@@ -285,6 +303,7 @@ describe('prepared-route prompt admission', () => {
     release.resolve(undefined)
     await h.agent.whenIdle()
     expect(h.capable.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const types = h.agent.session.snapshotEvents().map(event => event.type)
     expect(types.filter(type => type === 'step/start' || type === 'step/end')).toEqual(['step/start', 'step/end'])
     expect(types.filter(type => ['system/message', 'user/message', 'request/header'].includes(type))).toEqual([])
@@ -303,6 +322,7 @@ describe('prepared-route prompt admission', () => {
     expect(h.plain.requests).toHaveLength(0)
     expect(h.capable.requests[1]).toMatchObject({ provider: 'capable', temperature: 0.5, maxTokens: 100 })
     expect(systemTexts(h.capable.requests[1]!)).toHaveLength(2)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(h.agent.session.snapshotEvents().filter(event => event.type === 'user/message')).toHaveLength(2)
   })
 })

+ 1 - 0
packages/core/agent-loop/tests/tool-calls.spec.ts

@@ -42,6 +42,7 @@ function waitForIdle(ctx: Context, agent: Agent): Promise<void> {
 }
 
 function events(agent: Agent): readonly SessionEvent[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return agent.session.snapshotEvents()
 }
 

+ 8 - 0
packages/core/agent-loop/tests/tool-order.spec.ts

@@ -69,6 +69,7 @@ async function runTurn(registrationOrder: string[], toolOrder?: SystemPromptConf
 describe('loop-level canonical tool order', () => {
   it('logs the request/header with tools in canonical order, not registration order', async () => {
     const { agent, adapter } = await runTurn(['zulu', 'alpha', 'mike'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const header = foldRequestHeader(agent.session.snapshotEvents())
     expect(header?.tools?.map(tool => tool.name)).toEqual(['alpha', 'mike', 'zulu'])
     // The dispatched request is built FROM the logged header (whose tools the
@@ -82,6 +83,7 @@ describe('loop-level canonical tool order', () => {
   it('produces the same header order for any registration order', async () => {
     const first = await runTurn(['alpha', 'mike', 'zulu'])
     const second = await runTurn(['zulu', 'mike', 'alpha'])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const names = (run: typeof first) => foldRequestHeader(run.agent.session.snapshotEvents())?.tools?.map(tool => tool.name)
     expect(names(first)).toEqual(['alpha', 'mike', 'zulu'])
     expect(names(second)).toEqual(names(first))
@@ -89,6 +91,7 @@ describe('loop-level canonical tool order', () => {
 
   it('honors a configured toolOrder in the logged header and the dispatched request', async () => {
     const { agent, adapter } = await runTurn(['alpha', 'zulu', 'mike'], ['zulu', TOOL_ORDER_REST])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const header = foldRequestHeader(agent.session.snapshotEvents())
     expect(header?.tools?.map(tool => tool.name)).toEqual(['zulu', 'alpha', 'mike'])
     expect(adapter.requests[0]?.tools?.map(tool => tool.name)).toEqual(['zulu', 'alpha', 'mike'])
@@ -103,10 +106,15 @@ describe('loop-level canonical tool order', () => {
     agent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } }))
     await waitForIdle(ctx, agent)
     expect(adapter.requests).toHaveLength(0)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldRequestHeader(agent.session.snapshotEvents())).toBeUndefined()
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'turn/start')).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'turn/end')).toBe(true)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'step/start')).toBe(false)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(agent.session.snapshotEvents().some(e => e.type === 'step/end')).toBe(false)
   })
 })

+ 13 - 0
packages/core/agent/tests/consumed-work.spec.ts

@@ -40,6 +40,7 @@ describe('foldConsumedWork', () => {
     const session = Session.create(SessionId('empty'))
     accept(session, 'queued')
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents())).toEqual({ droppedUnrun: false })
   })
 
@@ -48,6 +49,7 @@ describe('foldConsumedWork', () => {
     steppedTurn(session, 1, { kind: 'completed' })
     steppedTurn(session, 2, { kind: 'max-tokens' })
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).end?.data)
       .toEqual({ turn: 2, reason: { kind: 'max-tokens' } })
   })
@@ -61,6 +63,7 @@ describe('foldConsumedWork', () => {
     claim(session)
     session.append('turn/end', { turn: 2, reason: { kind: 'error', error: { message: 'ENOSPC', code: 'UNKNOWN' } } })
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).end?.data.turn).toBe(2)
   })
 
@@ -71,6 +74,7 @@ describe('foldConsumedWork', () => {
     claim(session)
     session.append('turn/end', { turn: 2, reason: { kind: 'aborted', reason: { kind: 'user' } } })
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).end?.data.turn).toBe(2)
   })
 
@@ -85,6 +89,7 @@ describe('foldConsumedWork', () => {
     session.append('turn/end', { turn: 4, reason: { kind: 'blocked' } })
 
     // None of these turns describes work: they opened, found nothing of their own, and closed.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).end?.data.turn).toBe(1)
   })
 
@@ -97,6 +102,7 @@ describe('foldConsumedWork', () => {
 
     // Rejection does not retain the claimed messages, so the `blocked` end is
     // the only account of input that will never run.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).end?.data.turn).toBe(2)
   })
 
@@ -109,6 +115,7 @@ describe('foldConsumedWork', () => {
 
     // An emptied claim ran nothing and dropped nothing: a listener rewrote the
     // batch away, which is not this log's account of the work.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).end?.data.turn).toBe(1)
   })
 
@@ -120,6 +127,7 @@ describe('foldConsumedWork', () => {
     claim(session)
     session.append('turn/end', { turn: 2, reason: { kind: 'aborted', reason: { kind: 'user' } } })
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).end?.data.turn).toBe(1)
   })
 
@@ -130,7 +138,9 @@ describe('foldConsumedWork', () => {
     cancelPending(session)
 
     // No turn opened over it, so only the cancellation says the work was cut short.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents())).toEqual({
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       end: session.snapshotEvents().find(event => event.type === 'turn/end'),
       droppedUnrun: true,
     })
@@ -143,6 +153,7 @@ describe('foldConsumedWork', () => {
       target: 'next-turn', start: 0, removedCount: 1, inserted: [message('rewritten')], outcome: 'canceled',
     })
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents()).droppedUnrun).toBe(false)
   })
 
@@ -152,7 +163,9 @@ describe('foldConsumedWork', () => {
     cancelPending(session)
     steppedTurn(session, 2, { kind: 'completed' })
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(foldConsumedWork(session.snapshotEvents())).toEqual({
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       end: session.snapshotEvents().findLast(event => event.type === 'turn/end'),
       droppedUnrun: false,
     })

+ 4 - 0
packages/core/session/src/index.ts

@@ -649,6 +649,7 @@ export class Session {
    * @returns a fresh array containing child-owned events in log order.
    */
   ownEvents(): readonly SessionEvent[] {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     return this.snapshotEvents(this.inheritedEventCount)
   }
 
@@ -1214,6 +1215,7 @@ export class SessionStore extends Service {
   }
 
   private _forkSeed(session: Session, requestedBoundary: SessionSeq | undefined): readonly SessionEvent[] {
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const lastEvent = session.snapshotEvents().at(-1)
     let boundary: SessionSeq
     if (requestedBoundary !== undefined) {
@@ -1236,6 +1238,7 @@ export class SessionStore extends Service {
       )
     }
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const boundaryEvent = session.eventAt(boundary)
     if (boundaryEvent === undefined || boundaryEvent.seq !== boundary) {
       throw new SessionForkError(
@@ -1243,6 +1246,7 @@ export class SessionStore extends Service {
         'INVALID_BOUNDARY',
       )
     }
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const events = session.snapshotEvents(SessionLogOffset(0), SessionLogOffset(boundary + 1))
     const lastTurnBoundary = events
       .findLast(event => event.type === 'turn/start' || event.type === 'turn/end')

+ 1 - 0
packages/core/session/src/invariant.ts

@@ -210,6 +210,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant
   const seedSession = (session: Session): SessionTrace => {
     const trace = freshTrace()
     traces.set(session, trace)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     for (const event of session.snapshotEvents()) {
       applyTransition(trace, validateEvent(trace, event, fail))
     }

+ 5 - 0
packages/core/session/tests/canonical-envelopes.spec.ts

@@ -121,6 +121,7 @@ describe('canonical event payload acceptance', () => {
         expect(copy).toEqual(event)
         if (path === 'adopt' || path === 'snapshot') expect(accept(copy)).toEqual(event)
       }
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(Session.create(id, [event]).eventAt(SessionSeq(0))).toEqual(event)
     }
   })
@@ -178,6 +179,7 @@ describe('canonical event payload acceptance', () => {
       const prepared = ctx.sessions.prepare(id, {
         seed: [event], meta: { ...header }, inheritedEventCount: SessionLogOffset(0), eventState,
       })
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(prepared.eventAt(SessionSeq(0))).toBe(event)
       expect(prepared.requestHeader()).toEqual({ config })
       expect(ctx.sessions.get(id)).toBeUndefined()
@@ -203,6 +205,7 @@ describe('canonical event payload acceptance', () => {
       const published = vi.fn()
       ctx.on('session/created', created)
       ctx.on('session/event', published)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const events = session.snapshotEvents()
       const messages = session.deriveMessages()
       for (const { event } of invalidData) {
@@ -210,6 +213,7 @@ describe('canonical event payload acceptance', () => {
         expect(() => ctx.sessions.create(SessionId('invalid-seed'), { seed: [event] })).toThrow()
         expect(ctx.sessions.get(SessionId('invalid-seed'))).toBeUndefined()
         expect(session.seq).toBe(0)
+        // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
         expect(session.snapshotEvents()).toBe(events)
         expect(session.surface.nodes).toEqual([])
         expect(session.surface.replaceGeneration).toBe(0)
@@ -279,6 +283,7 @@ describe('canonical event-local surface metadata', () => {
       expect(() => accept(structuredClone(event))).not.toThrow()
     }
     const session = Session.create(id, [event])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.eventAt(SessionSeq(0))).toEqual(event)
     expect(session.surface.nodes).toEqual([])
     expect(session.deriveMessages()).toEqual([])

+ 1 - 0
packages/core/session/tests/derived-cache.spec.ts

@@ -16,6 +16,7 @@ function userText(session: Session, text: string): void {
 
 /** From-scratch oracle: replay the log into a fresh session and derive. */
 function scratch(session: Session): unknown {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return Session.create(SessionId(`${session.id}-scratch-${session.seq}`), session.snapshotEvents()).deriveMessages()
 }
 

+ 13 - 0
packages/core/session/tests/fork.spec.ts

@@ -47,6 +47,7 @@ function firstUserMessage(events: readonly SessionEvent[]): SessionEvent<'user/m
 }
 
 function lastSeq(session: Session): SessionSeq {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   const event = session.snapshotEvents().at(-1)
   if (event === undefined) throw new Error('missing last event')
   return event.seq
@@ -54,6 +55,7 @@ function lastSeq(session: Session): SessionSeq {
 
 /** A seeded child's fork-inherited prefix. */
 function inherited(session: Session): readonly SessionEvent[] {
+  // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
   return session.snapshotEvents(SessionLogOffset(0), session.inheritedEventCount)
 }
 
@@ -81,13 +83,19 @@ describe('SessionStore.fork', () => {
 
     const child = sessions.fork(SessionId('parent'), undefined, SessionId('child'))
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(inherited(child)).toEqual(source.snapshotEvents())
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child.snapshotEvents()).not.toBe(source.snapshotEvents())
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(child.snapshotEvents()[1]).not.toBe(source.snapshotEvents()[1])
     expect(() => {
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       firstUserMessage(child.snapshotEvents()).data.content[0] = { type: 'text', text: 'child mutation' }
     }).toThrow(TypeError)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(firstUserMessage(source.snapshotEvents()).data.content).toEqual([{ type: 'text', text: 'hello' }])
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(firstUserMessage(child.snapshotEvents()).data.content).toEqual([{ type: 'text', text: 'hello' }])
     expect(child.header).toMatchObject({
       id: SessionId('child'),
@@ -106,6 +114,7 @@ describe('SessionStore.fork', () => {
 
     const child = sessions.fork(source, undefined, SessionId('log-only-child'))
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(inherited(child)).toEqual(source.snapshotEvents())
     expect(inherited(child).at(-1)).toMatchObject({
       type: 'test/log-only',
@@ -123,6 +132,7 @@ describe('SessionStore.fork', () => {
 
     const child = sessions.fork(source, firstBoundary, SessionId('child-from-first'))
 
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(inherited(child)).toEqual(source.snapshotEvents().slice(0, firstBoundary + 1))
     expect(child.inheritedEventCount).toBe(firstBoundary + 1)
     expect(child.deriveMessages()).toEqual([{
@@ -166,9 +176,12 @@ describe('SessionStore.fork', () => {
     const child = sessions.fork(parent, undefined, SessionId('bracket-child'))
 
     // Parent: no end-seed event follows the bracket, so its owner treats it as live.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(parent.snapshotEvents().at(-1)).toBe(open)
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(parent.snapshotEvents().some(event => event.type === 'session/end-seed')).toBe(false)
     // Child: the same bracket is before end-seed, so it belongs to the seed.
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     const boundary = child.snapshotEvents().at(-1)
     expect(boundary).toMatchObject({ type: 'session/end-seed' })
     expect(boundary!.seq).toBeGreaterThan(open.seq)

+ 2 - 0
packages/core/session/tests/invariant.spec.ts

@@ -83,6 +83,7 @@ describe('session-log invariants', () => {
     expect(() => session.append('turn/start', {
       turn: 1,
     })).toThrow('later dispatch veto')
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(session.snapshotEvents()).toEqual([])
     expect(() => {
       session.append('turn/start', { turn: 1 })
@@ -428,6 +429,7 @@ describe('session-log invariants', () => {
     const open = ctx.sessions.create(SessionId('inherited-inside-open-turn'), { seed: [
       { type: 'turn/start', seq: SessionSeq(0), time: 1, data: { turn: 1 } },
     ] })
+    // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
     expect(open.snapshotEvents().map(event => event.type)).toEqual(['turn/start', 'session/end-seed'])
     // Still open afterwards: the boundary moves no cursor.
     expect(() => open.append('turn/start', { turn: 2 }))

+ 8 - 0
packages/core/session/tests/properties.spec.ts

@@ -107,6 +107,7 @@ describe('Session properties', () => {
   it('seq is strictly monotonic and zero-based contiguous', () => {
     fc.assert(fc.property(logArb, (events) => {
       const session = build(events)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       session.snapshotEvents().forEach((event, i) => { expect(event.seq).toBe(i) })
       expect(session.seq).toBe(events.length)
     }))
@@ -115,9 +116,11 @@ describe('Session properties', () => {
   it('replay-from-seed reproduces the derivation identically', () => {
     fc.assert(fc.property(logArb, (events) => {
       const original = build(events)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const replayed = Session.create(SessionId(`replay-${counter++}`), original.snapshotEvents())
       expect(replayed.deriveMessages()).toEqual(original.deriveMessages())
       // Every explicit replay grows by exactly one log-only boundary.
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(replayed.snapshotEvents().slice(0, original.seq)).toEqual(original.snapshotEvents())
       expect(replayed.seq).toBe(original.seq + 1)
     }))
@@ -126,9 +129,12 @@ describe('Session properties', () => {
   it('replaying a log that already ends in end-seed adds no further marker', () => {
     fc.assert(fc.property(logArb, (events) => {
       const original = build(events)
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const once = Session.create(SessionId(`idem-a-${counter++}`), original.snapshotEvents())
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const twice = Session.create(SessionId(`idem-b-${counter++}`), once.snapshotEvents())
       // Lazy resume makes browsing a pickup, so this must not grow per open.
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(twice.snapshotEvents()).toEqual(once.snapshotEvents())
     }))
   })
@@ -162,6 +168,7 @@ describe('Session properties', () => {
     fc.assert(fc.property(logArb, (events) => {
       const session = build(events)
       const messages = session.deriveMessages()
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       const before = structuredClone(session.snapshotEvents())
       for (const m of messages) {
         expect(['user', 'assistant', 'system']).toContain(m.role)
@@ -170,6 +177,7 @@ describe('Session properties', () => {
         expect(Object.isFrozen(m)).toBe(true)
         expect(() => { m.content.push({ type: 'text', text: 'mutation' }) }).toThrow(TypeError)
       }
+      // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
       expect(session.snapshotEvents()).toEqual(before)
     }))
   })

Някои файлове не бяха показани, защото твърде много файлове са промени