From 5cfc765ff6f2d1a99588f33a95bd4c4501e5771a Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 12:11:13 +0800 Subject: [PATCH 01/20] docs(session): deprecate direct event readers --- docs/subsystems/session.i18n.yaml | 4 ++-- docs/subsystems/session.md | 3 +++ docs/subsystems/session.zh.md | 3 +++ packages/core/session/README.i18n.yaml | 4 ++-- packages/core/session/README.md | 2 +- packages/core/session/README.zh.md | 2 +- packages/core/session/src/index.ts | 3 +++ 7 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/subsystems/session.i18n.yaml b/docs/subsystems/session.i18n.yaml index 26fee2e323..4d4f5bc022 100644 --- a/docs/subsystems/session.i18n.yaml +++ b/docs/subsystems/session.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/subsystems/session.md -session.md: de0930ea7effcba69bc1f9a4dd405ceda23919d7 -session.zh.md: ec15dbdec3a8887d8fa87c9698b8ad14699a534b +session.md: 077e90c66abe3b5f3f24d14c3fb25f356d887fa8 +session.zh.md: 6b9ceb85a276b0221c1e6ff474fbcdea77dff1fd diff --git a/docs/subsystems/session.md b/docs/subsystems/session.md index de0930ea7e..077e90c66a 100644 --- a/docs/subsystems/session.md +++ b/docs/subsystems/session.md @@ -473,6 +473,7 @@ declare class Session { ): Session; /** * Return the immutable event stored at one exact sequence number. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @param seq - event sequence number. * @returns the accepted event, or undefined when the log does not contain it. */ @@ -481,6 +482,7 @@ declare class Session { * Materialize an immutable snapshot of a half-open event sequence range. * A full current snapshot is reused until the next append; every previously * returned snapshot remains stable after later appends. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @param fromSeq - non-negative inclusive sequence number; defaults to the log start. * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end. * @returns a frozen array of the selected deeply frozen events. @@ -491,6 +493,7 @@ declare class Session { ): readonly SessionEvent[]; /** * Return this Session's events after its fork-inherited prefix. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @returns a fresh array containing child-owned events in log order. */ ownEvents(): readonly SessionEvent[]; diff --git a/docs/subsystems/session.zh.md b/docs/subsystems/session.zh.md index ec15dbdec3..6b9ceb85a2 100644 --- a/docs/subsystems/session.zh.md +++ b/docs/subsystems/session.zh.md @@ -475,6 +475,7 @@ declare class Session { ): Session; /** * Return the immutable event stored at one exact sequence number. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @param seq - event sequence number. * @returns the accepted event, or undefined when the log does not contain it. */ @@ -483,6 +484,7 @@ declare class Session { * Materialize an immutable snapshot of a half-open event sequence range. * A full current snapshot is reused until the next append; every previously * returned snapshot remains stable after later appends. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @param fromSeq - non-negative inclusive sequence number; defaults to the log start. * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end. * @returns a frozen array of the selected deeply frozen events. @@ -493,6 +495,7 @@ declare class Session { ): readonly SessionEvent[]; /** * Return this Session's events after its fork-inherited prefix. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @returns a fresh array containing child-owned events in log order. */ ownEvents(): readonly SessionEvent[]; diff --git a/packages/core/session/README.i18n.yaml b/packages/core/session/README.i18n.yaml index fd052eb9f9..ed9b39bdff 100644 --- a/packages/core/session/README.i18n.yaml +++ b/packages/core/session/README.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 packages/core/session/README.md -README.md: a06b22b5e8f48743047c68e883edd496fbc6eb77 -README.zh.md: 755894b4c30f0a3807f472cece32685d102efbeb +README.md: 402f3536402d78d0a70135290092cd7086706fb2 +README.zh.md: 303af9a878909f99e20dec1052b6592da7c8c415 diff --git a/packages/core/session/README.md b/packages/core/session/README.md index a06b22b5e8..402f353640 100644 --- a/packages/core/session/README.md +++ b/packages/core/session/README.md @@ -55,7 +55,7 @@ Append, seed/restore, and event adoption/snapshot reject any `header.system` and ### Read the log -`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. Callers that only need a length or one event use `seq` or `eventAt()`. +`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. `eventAt()`, `snapshotEvents()`, and `ownEvents()` are deprecated: existing logic may remain unmigrated for now, but new calls are prohibited. Callers that only need a length use `seq`. Session log positions use two numeric types. `SessionSeq` identifies an existing event or inclusive event watermark; `SessionLogOffset` identifies a gap, prefix length, or read boundary and may equal the event count. `SessionSeqCursor` adds the `-1` “no event yet” value, while `OptionalSessionSeq` uses `null` when absence is data. The constructors validate non-negative safe integers, and the brands disappear at runtime, so durable JSON and wire values remain ordinary numbers. diff --git a/packages/core/session/README.zh.md b/packages/core/session/README.zh.md index 755894b4c3..303af9a878 100644 --- a/packages/core/session/README.zh.md +++ b/packages/core/session/README.zh.md @@ -55,7 +55,7 @@ session.deriveMessages() // the derived model history ### 读取日志 -`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)` 会物化半开区间的冻结稳定快照;当前完整快照会缓存到下一次追加。只需要长度或单个事件的调用方使用 `seq` 或 `eventAt()`。 +`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)` 会物化半开区间的冻结稳定快照;当前完整快照会缓存到下一次追加。`eventAt()`、`snapshotEvents()` 和 `ownEvents()` 已弃用:现有逻辑可以暂不迁移,但禁止新增调用。只需要长度的调用方使用 `seq`。 会话日志位置使用两种数字类型。`SessionSeq` 标识已有事件或包含端点的事件水位;`SessionLogOffset` 标识间隙、前缀长度或读取边界,并且可以等于事件数量。`SessionSeqCursor` 添加 `-1` 这个“尚无事件”值,`OptionalSessionSeq` 则在缺失本身属于数据时使用 `null`。构造函数会校验非负安全整数,品牌在运行时会被擦除,因此持久 JSON 与 wire 值仍是普通数字。 diff --git a/packages/core/session/src/index.ts b/packages/core/session/src/index.ts index 9db8327409..68577578dd 100644 --- a/packages/core/session/src/index.ts +++ b/packages/core/session/src/index.ts @@ -615,6 +615,7 @@ export class Session { /** * Return the immutable event stored at one exact sequence number. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @param seq - event sequence number. * @returns the accepted event, or undefined when the log does not contain it. */ @@ -626,6 +627,7 @@ export class Session { * Materialize an immutable snapshot of a half-open event sequence range. * A full current snapshot is reused until the next append; every previously * returned snapshot remains stable after later appends. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @param fromSeq - non-negative inclusive sequence number; defaults to the log start. * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end. * @returns a frozen array of the selected deeply frozen events. @@ -643,6 +645,7 @@ export class Session { /** * Return this Session's events after its fork-inherited prefix. + * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. * @returns a fresh array containing child-owned events in log order. */ ownEvents(): readonly SessionEvent[] { From aa491acc2939b6bfe87aef70290af4058fa069b2 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 12:20:38 +0800 Subject: [PATCH 02/20] docs(session): record synchronous history read deprecation policy --- ...ev-invariants-over-deep-readonly.i18n.yaml | 4 +- ...06-11-dev-invariants-over-deep-readonly.md | 4 +- ...11-dev-invariants-over-deep-readonly.zh.md | 4 +- ...-synchronous-session-event-reads.i18n.yaml | 6 +++ ...precate-synchronous-session-event-reads.md | 45 +++++++++++++++++++ ...cate-synchronous-session-event-reads.zh.md | 45 +++++++++++++++++++ ...2026-07-06-recallable-compaction.i18n.yaml | 4 +- .../2026-07-06-recallable-compaction.md | 4 +- .../2026-07-06-recallable-compaction.zh.md | 4 +- packages/core/session/README.i18n.yaml | 4 +- packages/core/session/README.md | 2 +- packages/core/session/README.zh.md | 2 +- 12 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 .agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml create mode 100644 .agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md create mode 100644 .agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md diff --git a/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.i18n.yaml b/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.i18n.yaml index 5b233e16db..6578c0b2a2 100644 --- a/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.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-06-11-dev-invariants-over-deep-readonly.md -2026-06-11-dev-invariants-over-deep-readonly.md: 9adb741db1e26e3a9f750fed60524351159cfeaa -2026-06-11-dev-invariants-over-deep-readonly.zh.md: d352eca985b6594ec5d10c9b73c2ecc18ea458b5 +2026-06-11-dev-invariants-over-deep-readonly.md: 0784960397f4d198e136e284ef9232b4ed3e2c2c +2026-06-11-dev-invariants-over-deep-readonly.zh.md: 7183813130560bf82426c8719570b668ba86b7e5 diff --git a/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md b/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md index 9adb741db1..0784960397 100644 --- a/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md +++ b/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md @@ -22,7 +22,7 @@ Responsibility is split between an always-on storage boundary and optional devel `Session` accepts an event only after one recursive pass has materialized a lossless JSON snapshot. That pass rejects unsupported values and produces the exact detached record that enters the log, so validation and storage cannot observe different values from a stateful getter or retain caller-owned nested references. -The accepted event and all of its descendants are deep-frozen before publication. `append()` returns that owned frozen event, and `session/event` observers and `eventAt(seq)` receive the same record. `snapshotEvents(fromSeq?, toSeqExclusive?)` returns a frozen array snapshot; a previously returned array does not grow after a later append. `seq` and `eventAt()` avoid array materialization when a caller needs only the current length or one event. Seed records pass through the same validation, snapshot, and freeze boundary before construction succeeds. +The accepted event and all of its descendants are deep-frozen before publication. `append()` returns that owned frozen event, and `session/event` observers and `eventAt(seq)` receive the same record. `snapshotEvents(fromSeq?, toSeqExclusive?)` returns a frozen array snapshot; a previously returned array does not grow after a later append. `seq` reads the current length without materializing an array. Synchronous historical readers are deprecated under the [event-read policy](2026-09-09-deprecate-synchronous-session-event-reads.md). Seed records pass through the same validation, snapshot, and freeze boundary before construction succeeds. This guarantee belongs in `Session`, not in an optional listener, because every composition relies on trustworthy history. A production deployment, a focused test, or a custom embedding receives the same storage semantics whether or not development support plugins are registered. @@ -53,7 +53,7 @@ Detaching `deriveMessages()` would protect the most common request path but leav ## Consequences - Every accepted live or seeded session event is detached from caller-owned inputs and deeply immutable before any observer can receive it. -- `snapshotEvents()` exposes stable immutable snapshots instead of the private growing array; `seq` and `eventAt()` serve scalar reads without copying that array. +- Existing `snapshotEvents()` and `eventAt()` callers retain immutable read results while their migration is deferred; `seq` reads the log length without copying the array. - Request-side mutation cannot reach stored history through derived messages. - Development builds can enable relational assertions without changing storage behavior, and disposing or filtering a companion does not weaken log immutability. - `dsh-invariants` configures global enablement plus package allow/block regex lists; each check remains owned and tested by its product package. diff --git a/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.zh.md b/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.zh.md index d352eca985..7183813130 100644 --- a/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.zh.md +++ b/.agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.zh.md @@ -22,7 +22,7 @@ TypeScript readonly 类型不是充分的运行时边界。它们在程序运行 `Session` 仅在一次递归遍历完成无损 JSON 快照的物化之后才接受事件。该遍历拒绝不支持的值,并产出进入日志的已分离的确切记录,因此验证与存储不会从有状态的 getter 观察到不同的值,也不会保留调用方拥有的嵌套引用。 -被接受的事件及其所有后代在发布前被深度冻结。`append()` 返回由 Session 拥有的冻结事件,`session/event` 观察者和 `eventAt(seq)` 接收同一记录。`snapshotEvents(fromSeq?, toSeqExclusive?)` 返回冻结的数组快照;先前返回的数组不会因后续 append 而增长。调用方只需要当前长度或单个事件时,`seq` 和 `eventAt()` 不会物化数组。种子记录在构造成功前经过相同的验证、快照与冻结边界。 +被接受的事件及其所有后代在发布前被深度冻结。`append()` 返回由 Session 拥有的冻结事件,`session/event` 观察者和 `eventAt(seq)` 接收同一记录。`snapshotEvents(fromSeq?, toSeqExclusive?)` 返回冻结的数组快照;先前返回的数组不会因后续 append 而增长。`seq` 无需物化数组即可读取当前长度。同步历史读取方法按[事件读取策略](2026-09-09-deprecate-synchronous-session-event-reads.zh.md)弃用。种子记录在构造成功前经过相同的验证、快照与冻结边界。 此保证属于 `Session` 而非可选监听器,因为每种组合都依赖可信的历史。无论是否注册了开发支持插件,生产部署、聚焦测试或自定义嵌入都获得相同的存储语义。 @@ -53,7 +53,7 @@ TypeScript readonly 类型不是充分的运行时边界。它们在程序运行 ## 后果 - 每个被接受的实时或种子会话事件在任何观察者接收之前,都已从调用方拥有的输入中分离并深度不可变。 -- `snapshotEvents()` 暴露稳定的不可变快照,而非持续增长的私有数组;`seq` 和 `eventAt()` 为标量读取提供无需复制数组的路径。 +- 现有 `snapshotEvents()` 和 `eventAt()` 调用方在暂缓迁移期间仍获得不可变的读取结果;`seq` 无需复制数组即可读取日志长度。 - 请求侧的修改无法通过派生消息触及已存储的历史。 - 开发构建可以启用关系断言而不改变存储行为;dispose 或过滤一个配套插件不会削弱日志不可变性。 - `dsh-invariants` 配置全局启用状态以及包名允许/阻止 regex 列表;每项检查仍由其产品包拥有并测试。 diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml new file mode 100644 index 0000000000..6af464d03b --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# 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 diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md new file mode 100644 index 0000000000..b0bc1e81ad --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md @@ -0,0 +1,45 @@ +# Agent Note: Deprecate synchronous reads of arbitrary Session events + +Status: implemented + +English | [中文](2026-09-09-deprecate-synchronous-session-event-reads.zh.md) + +## Problem + +Synchronous access to arbitrary event positions makes consumers depend on the complete Session event sequence being immediately available in memory. The storage direction is to stop retaining that complete sequence in memory. Once historical events require storage I/O, the runtime cannot preserve the same synchronous read guarantee without retaining the history or blocking on storage. + +New callers increase that dependency even when they read only one old event. Repeated history scans after resume also make ordinary domain logic depend on historical storage instead of the state it actually needs. + +## Decision + +All operations that synchronously read arbitrary positions or ranges of Session event history are deprecated, including `Session.eventAt()`, `Session.snapshotEvents()`, and `Session.ownEvents()`. Existing logic may remain unmigrated for now, but new calls are prohibited. New aliases or wrappers that expose the same synchronous historical access are prohibited as well. + +The three methods carry this rule in `@deprecated` JSDoc. This is an API-use decision; the current Session implementation still retains the complete event sequence in memory. + +### State needed after resume + +Design durable event fields and Session projections together so each domain can reconstruct the state its consumers need. Restore that state during resume, then maintain it incrementally from newly committed events. After resume, ordinary logic reads the projection or processes the delivered current event instead of looking back through historical events. Reading already-maintained projection state synchronously does not require arbitrary access to the event log. + +Historical content presented on demand uses explicit asynchronous pagination and progressive loading, with each read limited to the requested window. Loading the complete sequence behind a synchronous helper preserves the dependency this decision removes. + +### Operations that require complete history + +Fork and a small number of operations may genuinely need a complete historical sequence or inherited prefix. Their need for those records remains valid and requires an explicit storage read. It does not require the whole sequence to remain resident or grant an exception for new calls to deprecated synchronous readers. Each such consumer must establish why its result requires the complete sequence rather than projected state or a limited historical window. + +## Alternatives considered + +**Keep synchronous single-event reads while deprecating only full snapshots.** A single requested event may also be absent from memory. Restricting the result size does not remove the storage dependency, and helpers such as `ownEvents()` retain the same assumption for a suffix. + +**Keep the complete sequence resident to preserve the read APIs.** This lets consumer convenience dictate Session memory retention and prevents the intended storage design. Projections preserve required state, while explicit historical reads preserve access to the records themselves. + +**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. + +## 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. + +## Related decisions + +The [session immutability decision](2026-06-11-dev-invariants-over-deep-readonly.md) continues to own event ownership and freezing; this decision supersedes its recommendation to use synchronous historical readers. The [required projection reader decision](2026-08-19-session-projection-mandatory-seam.md) continues to own missing-state failures and typed state reads. The [recallable compaction proposal](../../proposed/feature/2026-07-06-recallable-compaction.md) retains its recall design while replacing its proposed synchronous history access with paged reads. diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md new file mode 100644 index 0000000000..0c8fd24bb4 --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md @@ -0,0 +1,45 @@ +# Agent Note: 弃用对会话任意位置事件的同步读取 + +Status: implemented + +[English](2026-09-09-deprecate-synchronous-session-event-reads.md) | 中文 + +## 问题 + +同步访问任意事件位置,使消费方依赖完整会话事件序列在内存中随时可用。存储的演进方向是不再将完整序列保存在内存中。历史事件一旦需要存储 I/O,运行时若不继续保留历史或阻塞等待存储,就无法维持相同的同步读取保证。 + +即使只读取一个旧事件,新增调用也会加深这种依赖。恢复后反复扫描历史,还会使普通领域逻辑依赖历史存储,而不是它真正需要的状态。 + +## 决策 + +弃用所有同步读取会话事件历史任意位置或区间的操作,包括 `Session.eventAt()`、`Session.snapshotEvents()` 和 `Session.ownEvents()`。现有逻辑可以暂不迁移,但禁止新增调用。同样禁止新增暴露相同同步历史访问能力的别名或包装层。 + +这三个方法通过 `@deprecated` JSDoc 声明该规则。这是 API 使用决策;当前 Session 实现仍在内存中保留完整事件序列。 + +### 恢复后需要的状态 + +结合设计持久事件字段与会话投影,使每个领域都能重建其消费方所需的状态。在恢复期间还原这些状态,随后通过新提交的事件增量维护。恢复后,普通逻辑读取投影或处理当前收到的事件,而不是回头查找历史事件。同步读取已经维护好的投影状态,不需要任意访问事件日志。 + +按需展示的历史内容采用显式异步分页和渐进式加载,每次读取限制在请求的窗口内。在同步辅助方法背后加载完整序列,仍保留了本决策要消除的依赖。 + +### 确实需要完整历史的操作 + +fork 等少数操作可能确实需要完整历史序列或继承前缀。它们对这些记录的需求仍然成立,需要通过显式存储读取来满足。这不要求完整序列常驻内存,也不构成新增已弃用同步读取调用的例外。每个此类消费方都必须说明,为什么其结果需要完整序列,而不能只使用投影状态或局部历史窗口。 + +## 曾考虑的替代方案 + +**只弃用完整快照,保留同步单事件读取。** 单个被请求的事件也可能不在内存中。限制返回结果大小并不能消除存储依赖,`ownEvents()` 等辅助方法对后缀序列也保留着相同假设。 + +**为保留读取 API 而让完整序列常驻内存。** 这会让消费方的便利性决定会话的内存保留方式,阻碍预期的存储设计。投影保留所需状态,显式历史读取则保留对记录本身的访问能力。 + +**要求立即迁移所有现有调用方。** 本决策允许现有逻辑推迟迁移。禁止新增依赖可以限制剩余工作规模,无需将每个现有消费方都纳入同一次修改。 + +## 后果 + +新增领域行为必须使其事件数据与投影状态足以支持恢复后的执行。用户请求的历史仍可渐进式加载,确实需要完整历史的操作仍需设计存储读取路径。本决策不表示恢复或 fork 已经能够避免加载完整日志。 + +当前 `typescript/no-deprecated` lint 规则会同时报告已有调用与新增调用。JSDoc 记录了暂缓迁移的约定,但 lint 尚不能区分允许保留的现有调用与禁止新增的调用。文档检查校验与源码一致的 API 声明及双语记录,并不强制执行这种区分。 + +## 相关决策 + +[会话不可变性决策](2026-06-11-dev-invariants-over-deep-readonly.zh.md)继续负责事件所有权与冻结;本决策取代其中对同步历史读取方法的使用建议。[投影读取必需性决策](2026-08-19-session-projection-mandatory-seam.zh.md)继续负责缺失状态时的失败规则与类型化状态读取。[可回溯压缩提案](../../proposed/feature/2026-07-06-recallable-compaction.zh.md)保留其回溯设计,并将其中拟议的同步历史访问替换为分页读取。 diff --git a/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.i18n.yaml b/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.i18n.yaml index fded384582..d324ff433c 100644 --- a/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.i18n.yaml +++ b/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.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/proposed/feature/2026-07-06-recallable-compaction.md -2026-07-06-recallable-compaction.md: f0cf1b0602ad7dd5ae719fdd1e3b569b0bf5b448 -2026-07-06-recallable-compaction.zh.md: 8e8793f28be848e6231e86a3eaba099e68d9947e +2026-07-06-recallable-compaction.md: 07b89116b72a1c1aac2d79ee5aa41ff6e5d02a4c +2026-07-06-recallable-compaction.zh.md: 7347cb08c94665a6e5bb88655ec08d04921d08eb diff --git a/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.md b/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.md index f0cf1b0602..07b89116b7 100644 --- a/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.md +++ b/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.md @@ -46,7 +46,7 @@ A new package `@deepseek-ai/dsh-tool-recall` (consumer-only, over the `dsh-sessi - `history_read(checkpoint, offset?)` — renders the shadowed span of any checkpoint in the log, including superseded ones, as `User:`/`Assistant:`/`Tool result:` transcript, paginated by a configured budget with a continuation cursor. - `history_search(query, checkpoint?, limit?)` — case-insensitive literal scan over every shadowed span; returns snippets with checkpoint ids and coverage metadata (`scanned`/`matched`/`truncated`). The zero-match hint notes the scan is literal and points at direct `history_read` of a plausible checkpoint. -Both read `exec.agent.session.snapshotEvents()` (the tool-todo access pattern; non-agent callers rejected), render only surface-type message events, and return ordinary `tool/result`s — recalled bytes land at the context tail, logged, so reconstructability holds with no special casing. There is no new storage and no sidecar index: the session log stores the content, `compaction/summary.shadowedRange` and `shadowedSeqs` identify what each checkpoint replaced, and the tools read both. The tool schemas and the package's one system-prompt section are static strings; checkpoint ids reach the model only through footers. The transcript renderer moves from `compaction-basic` into `dsh-session`, shared by summarizer and tools. +Both use explicit asynchronous, paged history reads under the [synchronous event-read deprecation](../../implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md) (non-agent callers rejected), render only surface-type message events, and return ordinary `tool/result`s — recalled bytes land at the context tail, logged, so reconstructability holds with no special casing. There is no new storage and no sidecar index: the session log stores the content, `compaction/summary.shadowedRange` and `shadowedSeqs` identify what each checkpoint replaced, and the tools read both. The tool schemas and the package's one system-prompt section are static strings; checkpoint ids reach the model only through footers. The transcript renderer moves from `compaction-basic` into `dsh-session`, shared by summarizer and tools. ### Cache and cost @@ -86,7 +86,7 @@ Deferred until observation calls for them: - **One summarize call emitting all outputs** — rejected: the summarize path has no structured-output enforcement; parsing one free-text response apart is the fragile boundary the fail-closed design avoids. - **Model-chosen chunk boundaries** — deferred: parse-and-validate cost against unproven value; chunk policy sits behind config. - **Model-authored pointers** — rejected: pointers must be exact; deterministic assembly is. -- **FTS/vector index sidecar** — rejected in-session: the live log is in memory and bounded, a literal scan under budget suffices; an index earns its keep at cross-session scope. +- **FTS/vector index sidecar** — the rejection based on a resident, bounded live log is superseded by the [event-read policy](../../implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). Reassess the index choice against paged historical reads before implementing recall. - **Semantic search fallback / secondary-model extraction in the recall path** — rejected: an LLM or embedding call there breaks keyless replay determinism; recall stays a pure function of the log. - **Raw events instead of rendered transcript** — rejected: leaks log-only vocabulary and chunk noise; the model reads what a model once saw. - **Doing nothing (resume/fork as recovery)** — rejected: it makes recovery a human act. diff --git a/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.zh.md b/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.zh.md index 8e8793f28b..7347cb08c9 100644 --- a/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.zh.md +++ b/.agents/notes/proposed/feature/2026-07-06-recallable-compaction.zh.md @@ -46,7 +46,7 @@ Status: proposed - `history_read(checkpoint, offset?)`:把日志中任意检查点(包括已被取代的检查点)遮蔽的区段渲染为 `User:`/`Assistant:`/`Tool result:` transcript(文本记录),并按配置预算分页,提供续传游标。 - `history_search(query, checkpoint?, limit?)`:对每个被遮蔽区段进行不区分大小写的字面量扫描;返回带检查点 id 的片段与覆盖元数据(`scanned`/`matched`/`truncated`)。零匹配提示会说明扫描按字面量执行,并建议对可能的检查点直接使用 `history_read`。 -两个工具都读取 `exec.agent.session.snapshotEvents()`(沿用 tool-todo 访问模式;拒绝非 agent(智能体)调用方),只渲染表面类型的消息事件,并返回普通 `tool/result`:回溯字节会进入上下文尾部并记录到日志,因此无需特殊处理即可满足可重建性。系统不增加新存储或伴随索引:会话日志存储内容,`compaction/summary.shadowedRange` 和 `shadowedSeqs` 指明每个检查点替换了什么,这些工具读取两者。工具 schema 与该包唯一的系统提示词章节都是静态字符串;检查点 id 只会通过页脚抵达模型。transcript 渲染器从 `compaction-basic` 移入 `dsh-session`,供摘要器与工具共享。 +两个工具都按[同步事件读取弃用规则](../../implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md)使用显式异步分页历史读取(拒绝非 agent(智能体)调用方),只渲染表面类型的消息事件,并返回普通 `tool/result`:回溯字节会进入上下文尾部并记录到日志,因此无需特殊处理即可满足可重建性。系统不增加新存储或伴随索引:会话日志存储内容,`compaction/summary.shadowedRange` 和 `shadowedSeqs` 指明每个检查点替换了什么,这些工具读取两者。工具 schema 与该包唯一的系统提示词章节都是静态字符串;检查点 id 只会通过页脚抵达模型。transcript 渲染器从 `compaction-basic` 移入 `dsh-session`,供摘要器与工具共享。 ### 缓存与成本 @@ -86,7 +86,7 @@ Status: proposed - **一次摘要调用输出全部结果**:不予采纳,因为摘要路径没有结构化输出约束;解析一份自由文本响应并将其拆开,正是保守失败设计要避免的脆弱边界。 - **由模型选择分片边界**:延后实现,因为相对于未经证明的收益,解析与校验成本过高;分片策略由配置控制。 - **由模型编写指针**:不予采纳,因为指针必须精确,应由确定性代码组装。 -- **FTS/向量索引伴随存储**:在会话内不予采纳,因为实时日志已在内存中且大小有界,在预算内进行字面量扫描已经足够;只有跨会话范围才能证明索引的价值。 +- **FTS/向量索引伴随存储**:以实时日志常驻内存且大小有界为依据的否决,被[事件读取策略](../../implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md)取代。实现回溯前,需要针对分页历史读取重新评估索引选择。 - **回溯路径中的语义搜索回退/次级模型提取**:不予采纳,因为其中的 LLM 或嵌入调用会破坏无密钥回放的确定性;回溯必须保持为日志的纯函数。 - **使用原始事件而不是渲染后的 transcript**:不予采纳,因为这会泄漏仅日志可见的词汇与分片噪声;模型应读取模型曾经看到的内容。 - **什么都不做(用恢复/fork 补救)**:不予采纳,因为这会把恢复变成人工操作。 diff --git a/packages/core/session/README.i18n.yaml b/packages/core/session/README.i18n.yaml index ed9b39bdff..773d428da8 100644 --- a/packages/core/session/README.i18n.yaml +++ b/packages/core/session/README.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 packages/core/session/README.md -README.md: 402f3536402d78d0a70135290092cd7086706fb2 -README.zh.md: 303af9a878909f99e20dec1052b6592da7c8c415 +README.md: ebe4c1c29992b29b07ad38d7368ca5cef2253ee0 +README.zh.md: f5d0ac59f1276e4fe59c1cc449dcf6ce5a082aba diff --git a/packages/core/session/README.md b/packages/core/session/README.md index 402f353640..ebe4c1c299 100644 --- a/packages/core/session/README.md +++ b/packages/core/session/README.md @@ -55,7 +55,7 @@ Append, seed/restore, and event adoption/snapshot reject any `header.system` and ### Read the log -`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. `eventAt()`, `snapshotEvents()`, and `ownEvents()` are deprecated: existing logic may remain unmigrated for now, but new calls are prohibited. Callers that only need a length use `seq`. +`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. `eventAt()`, `snapshotEvents()`, and `ownEvents()` are deprecated: existing logic may remain unmigrated for now, but new calls are prohibited ([rationale](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md)). Callers that only need a length use `seq`. Session log positions use two numeric types. `SessionSeq` identifies an existing event or inclusive event watermark; `SessionLogOffset` identifies a gap, prefix length, or read boundary and may equal the event count. `SessionSeqCursor` adds the `-1` “no event yet” value, while `OptionalSessionSeq` uses `null` when absence is data. The constructors validate non-negative safe integers, and the brands disappear at runtime, so durable JSON and wire values remain ordinary numbers. diff --git a/packages/core/session/README.zh.md b/packages/core/session/README.zh.md index 303af9a878..f5d0ac59f1 100644 --- a/packages/core/session/README.zh.md +++ b/packages/core/session/README.zh.md @@ -55,7 +55,7 @@ session.deriveMessages() // the derived model history ### 读取日志 -`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)` 会物化半开区间的冻结稳定快照;当前完整快照会缓存到下一次追加。`eventAt()`、`snapshotEvents()` 和 `ownEvents()` 已弃用:现有逻辑可以暂不迁移,但禁止新增调用。只需要长度的调用方使用 `seq`。 +`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)` 会物化半开区间的冻结稳定快照;当前完整快照会缓存到下一次追加。`eventAt()`、`snapshotEvents()` 和 `ownEvents()` 已弃用:现有逻辑可以暂不迁移,但禁止新增调用([理由](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md))。只需要长度的调用方使用 `seq`。 会话日志位置使用两种数字类型。`SessionSeq` 标识已有事件或包含端点的事件水位;`SessionLogOffset` 标识间隙、前缀长度或读取边界,并且可以等于事件数量。`SessionSeqCursor` 添加 `-1` 这个“尚无事件”值,`OptionalSessionSeq` 则在缺失本身属于数据时使用 `null`。构造函数会校验非负安全整数,品牌在运行时会被擦除,因此持久 JSON 与 wire 值仍是普通数字。 From 37abcb74c588b57d373a6594faa268a0c40ccb26 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 12:33:53 +0800 Subject: [PATCH 03/20] chore(session): permit existing deprecated history reads --- ...-synchronous-session-event-reads.i18n.yaml | 4 +- ...precate-synchronous-session-event-reads.md | 4 +- ...cate-synchronous-session-event-reads.zh.md | 4 +- .../headless/tests/coding-task.e2e.ts | 1 + .../profiles/headless/tests/compaction.e2e.ts | 1 + .../profiles/headless/tests/full-loop.e2e.ts | 1 + .../tests/profiles/headless/tests/ptc.e2e.ts | 3 + .../profiles/headless/tests/resume.e2e.ts | 1 + .../profiles/headless/tests/todo-write.e2e.ts | 1 + .../tests/chat-continuous-conversation.e2e.ts | 1 + apps/web/tests/chat-long-interactions.e2e.ts | 11 +++ apps/web/tests/chat-scroll-fixture.ts | 1 + apps/web/tests/clickable-links-gallery.e2e.ts | 1 + apps/web/tests/complex-history.perf.ts | 1 + apps/web/tests/feedback-release.e2e.ts | 1 + .../tests/goal-command-presentation.e2e.ts | 1 + apps/web/tests/markdown-cjk-strong.e2e.ts | 1 + apps/web/tests/markdown-images.e2e.ts | 1 + .../tests/markdown-inline-code-links.e2e.ts | 1 + apps/web/tests/markdown-wide-table.e2e.ts | 1 + apps/web/tests/math-rendering.e2e.ts | 1 + apps/web/tests/minimal-preset.snapshot.ts | 1 + apps/web/tests/preset-migration.snapshot.ts | 1 + apps/web/tests/produced-file-mentions.e2e.ts | 1 + apps/web/tests/produced-files.e2e.ts | 1 + apps/web/tests/reference-composer.e2e.ts | 2 + apps/web/tests/scaffold.ts | 2 + apps/web/tests/schedule-after.e2e.ts | 8 +++ apps/web/tests/seeded-history.e2e.ts | 1 + apps/web/tests/settings-chrome.e2e.ts | 4 ++ apps/web/tests/subagent-interrupt-ui.e2e.ts | 1 + apps/web/tests/subagent-interrupt.e2e.ts | 2 + docs/config-catalog.i18n.yaml | 4 +- docs/config-catalog.md | 2 +- docs/config-catalog.zh.md | 2 +- packages/acp/acp/tests/bridge.spec.ts | 2 + packages/acp/acp/tests/turns.spec.ts | 5 ++ .../api/session-controller/src/commands.ts | 2 + .../api/session-controller/src/history.ts | 1 + packages/api/session-controller/src/index.ts | 1 + .../commands-queue-attachment.host.spec.ts | 1 + .../tests/session-cold.host.spec.ts | 3 + .../tests/session-fork.host.spec.ts | 6 ++ .../tests/session-projections.host.spec.ts | 3 + .../tests/session-rename.host.spec.ts | 1 + .../tests/transport.host.spec.ts | 1 + packages/bundle/headless/src/index.ts | 1 + .../spill-policy-terminal.client.spec.ts | 1 + .../tests/command-compact.spec.ts | 1 + .../tests/loader-composition.spec.ts | 1 + .../compaction/compaction-basic/src/region.ts | 9 ++- .../tests/compaction-basic.spec.ts | 29 ++++++++ .../tests/compaction-loop-repro.spec.ts | 12 ++++ .../tests/manual-compaction.spec.ts | 29 ++++++++ .../src/index.ts | 1 + .../tests/tool-result-pruner.spec.ts | 4 ++ .../compaction/compaction/src/invariant.ts | 1 + .../compaction/compaction/src/tool-pairing.ts | 1 + .../compaction/tests/compaction.spec.ts | 3 + .../compaction/tests/invariant.spec.ts | 8 +++ .../compaction/tests/tool-pairing.spec.ts | 1 + .../context/agent-instructions/src/index.ts | 2 + .../context/agent-instructions/src/state.ts | 1 + .../tests/agent-instructions.e2e.ts | 3 + .../tests/agent-instructions.spec.ts | 34 +++++++++ .../tests/loader-composition.spec.ts | 1 + .../tests/session-reference.spec.ts | 4 ++ packages/context/time-context/src/index.ts | 1 + .../context/time-context/src/invariant.ts | 2 + .../time-context/tests/time-context.spec.ts | 13 ++++ .../tmux-context/tests/tmux-context.spec.ts | 2 + packages/core/agent-loop/src/index.ts | 1 + packages/core/agent-loop/src/invariant.ts | 1 + .../core/agent-loop/src/runtime-context.ts | 2 + .../agent-loop/tests/agent-initiator.spec.ts | 1 + packages/core/agent-loop/tests/agent.spec.ts | 4 ++ packages/core/agent-loop/tests/cancel.spec.ts | 42 +++++++++++ .../tests/contract-regressions.spec.ts | 40 +++++++++++ .../agent-loop/tests/coverage-edges.spec.ts | 18 +++++ packages/core/agent-loop/tests/inbox.spec.ts | 4 ++ .../agent-loop/tests/interception.spec.ts | 1 + packages/core/agent-loop/tests/loop.spec.ts | 69 +++++++++++++++++++ .../core/agent-loop/tests/properties.spec.ts | 4 ++ .../agent-loop/tests/request-cache.e2e.ts | 1 + .../agent-loop/tests/request-error.spec.ts | 6 ++ .../agent-loop/tests/request-freeze.spec.ts | 3 + .../tests/request-reconstruction.spec.ts | 41 +++++++++++ packages/core/agent-loop/tests/resume.spec.ts | 9 +++ .../agent-loop/tests/scope-lifecycle.spec.ts | 3 + .../tests/system-prompt-admission.spec.ts | 20 ++++++ .../core/agent-loop/tests/tool-calls.spec.ts | 1 + .../core/agent-loop/tests/tool-order.spec.ts | 8 +++ .../core/agent/tests/consumed-work.spec.ts | 13 ++++ packages/core/session/src/index.ts | 4 ++ packages/core/session/src/invariant.ts | 1 + .../session/tests/canonical-envelopes.spec.ts | 5 ++ .../core/session/tests/derived-cache.spec.ts | 1 + packages/core/session/tests/fork.spec.ts | 13 ++++ packages/core/session/tests/invariant.spec.ts | 2 + .../core/session/tests/properties.spec.ts | 8 +++ .../core/session/tests/request-header.spec.ts | 1 + .../core/session/tests/sequence-types.spec.ts | 4 ++ packages/core/session/tests/session.spec.ts | 53 ++++++++++++++ packages/core/session/tests/surface.spec.ts | 20 ++++++ packages/core/tools/src/invariant.ts | 1 + packages/core/tools/tests/invariant.spec.ts | 1 + packages/core/tools/tests/ptc.spec.ts | 1 + .../experimental/agent-team/src/mailbox.ts | 1 + .../experimental/agent-team/src/roster.ts | 2 + .../agent-team/tests/invariant.spec.ts | 2 + .../agent-team/tests/persistence.spec.ts | 3 + .../agent-team/tests/team.spec.ts | 7 ++ .../agent-team/tests/test-session-query.ts | 1 + .../tool-agent-team/tests/tool-team.spec.ts | 1 + .../tests/command-feedback.spec.ts | 10 +++ .../tests/loader-composition.spec.ts | 4 ++ .../feedback/message-feedback/src/index.ts | 2 + .../message-feedback/tests/helpers.ts | 1 + .../tests/message-feedback.spec.ts | 9 +++ packages/fs/tool-fs/tests/fs-tools.e2e.ts | 1 + .../fs/tool-present/tests/present.spec.ts | 5 ++ .../command-goal/tests/command-goal.spec.ts | 4 ++ .../goal/goal-round-driver/src/invariant.ts | 2 + .../tests/goal-round-driver.spec.ts | 10 +++ packages/goal/goal/src/invariant.ts | 1 + packages/goal/goal/tests/goal.spec.ts | 28 ++++++++ packages/goal/goal/tests/projection.spec.ts | 1 + packages/goal/tool-goal/src/authority.ts | 1 + .../goal/tool-goal/tests/tool-goal.spec.ts | 2 + .../tests/repeat-tool-reminder.spec.ts | 3 + packages/hooks/hook-protocol/src/invariant.ts | 1 + .../hooks/hook-protocol/tests/events.spec.ts | 9 +++ .../hooks-claude-code/tests/bridge.spec.ts | 1 + .../hooks-claude-code/tests/coverage-cases.ts | 1 + .../hooks/hooks-codex/tests/bridge.spec.ts | 1 + .../hooks/hooks-codex/tests/coverage-cases.ts | 1 + .../interaction/commands/src/invariant.ts | 2 + .../commands/tests/commands.spec.ts | 5 ++ .../permission-presets/src/invariant.ts | 1 + .../tests/permission-presets.spec.ts | 18 +++++ .../tests/projection.spec.ts | 4 ++ .../interaction/user-approval/src/index.ts | 2 + .../user-approval/src/invariant.ts | 1 + .../user-approval/tests/approval.spec.ts | 9 +++ packages/llm/llm-retry/src/invariant.ts | 3 + .../tests/loader-composition.spec.ts | 1 + packages/llm/llm-retry/tests/retry.spec.ts | 25 +++++++ .../tests/transport-recovery.spec.ts | 17 +++++ packages/llm/token-meter/src/index.ts | 3 +- .../context-breakdown-projection.spec.ts | 6 ++ .../llm/token-meter/tests/token-meter.spec.ts | 6 ++ packages/plan/plan-mode/src/invariant.ts | 1 + .../plan/plan-mode/tests/integration.spec.ts | 6 ++ .../plan/plan-mode/tests/plan-mode.spec.ts | 39 +++++++++++ .../plan/plan-mode/tests/projection.spec.ts | 1 + .../preset/agent-presets/tests/remote.spec.ts | 1 + .../sandbox/sandbox-policy/src/invariant.ts | 1 + .../sandbox-policy/tests/policy.spec.ts | 2 + packages/schedule/schedule/src/index.ts | 1 + packages/schedule/schedule/src/invariant.ts | 3 + packages/schedule/schedule/src/runtime.ts | 1 + packages/schedule/schedule/src/tools.ts | 1 + .../schedule/schedule/tests/invariant.spec.ts | 5 ++ .../schedule/tests/jsonl-restart.spec.ts | 2 + .../schedule/schedule/tests/runtime.spec.ts | 11 +++ .../schedule/schedule/tests/tools.spec.ts | 9 +++ .../tests/loader-composition.host.spec.ts | 1 + .../session-query-sqlite/src/index.ts | 1 + .../session-query/session-query/src/corpus.ts | 2 + .../session-query/src/observation.ts | 1 + .../session-query/tests/session-query.spec.ts | 2 + .../tests/tool-session-query.spec.ts | 1 + .../session/session-log-deepseek/src/index.ts | 3 + .../session-log-deepseek/src/invariant.ts | 1 + .../tests/feedback-composition.spec.ts | 2 + .../session-log-deepseek/tests/upload.spec.ts | 4 ++ .../tests/multi-edge-publication.spec.ts | 1 + .../tests/v2-system-migration.spec.ts | 6 ++ .../session/session-projection/src/index.ts | 3 + .../session-telemetry-otel/src/index.ts | 1 + .../session-telemetry-otel/tests/otel.spec.ts | 15 ++++ .../session-telemetry/src/coordinator.ts | 1 + .../session-telemetry/tests/redact.spec.ts | 2 + .../session-telemetry/tests/telemetry.spec.ts | 8 +++ .../tests/provider.spec.ts | 1 + .../session-title-llm/tests/llm.spec.ts | 4 ++ packages/session/session-title/src/index.ts | 2 + .../session/session-title/src/invariant.ts | 2 + .../session-title/tests/projection.spec.ts | 1 + .../session-title/tests/provider.spec.ts | 3 + .../session-title/tests/rename.spec.ts | 3 + .../tests/service-contracts.spec.ts | 6 ++ .../session-title/tests/session-title.spec.ts | 5 ++ .../shell/tool-bash/tests/integration.spec.ts | 1 + packages/shell/tool-bash/tests/tools.spec.ts | 1 + packages/skill/tool-skill/src/index.ts | 1 + .../skill/tool-skill/tests/tool-skill.spec.ts | 1 + .../subagent-fork-in-process/src/index.ts | 1 + .../tests/multi-subagent.spec.ts | 4 ++ .../tests/subagent-fork-in-process.spec.ts | 8 +++ .../subagent-in-process-driver/src/index.ts | 1 + .../tests/inheritance.spec.ts | 13 ++++ .../tests/preset-inheritance.spec.ts | 1 + .../tests/structured.spec.ts | 6 ++ .../tests/subagent-in-process-driver.spec.ts | 8 +++ .../tests/spawn-in-process.e2e.ts | 1 + .../tests/subagent-spawn-in-process.spec.ts | 3 + packages/subagent/subagent/src/lifecycle.ts | 1 + .../subagent/tests/continuation.spec.ts | 12 ++++ packages/subagent/tool-subagent/src/index.ts | 1 + .../tests/model-selection-settings.spec.ts | 2 + .../terminal-bash/tests/index.spec.ts | 2 + .../tests/agent-loop-testkit.spec.ts | 1 + .../fixtures/workspace-context-compaction.ts | 2 + packages/todo/tool-todo/src/invariant.ts | 1 + .../todo/tool-todo/tests/integration.spec.ts | 3 + .../todo/tool-todo/tests/invariant.spec.ts | 2 + .../tests/loader-composition.spec.ts | 2 + .../todo/tool-todo/tests/tool-todo.spec.ts | 10 +++ .../workflow/tool-workflow/src/invariant.ts | 1 + .../tool-workflow/tests/tool-workflow.spec.ts | 7 ++ scripts/oxlint-contract.spec.ts | 53 ++++++++++++++ .../python-snapshot-workflow-order.spec.ts | 1 + 223 files changed, 1195 insertions(+), 12 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml index 6af464d03b..51e978ca70 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml +++ b/.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 diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md index b0bc1e81ad..0fcbfbc2e2 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md +++ b/.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 diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md index 0c8fd24bb4..d1f22b4833 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md +++ b/.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 声明及双语记录。 ## 相关决策 diff --git a/apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts b/apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts index bb0b4297b8..4b78389bb3 100644 --- a/apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts +++ b/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) diff --git a/apps/cli/tests/profiles/headless/tests/compaction.e2e.ts b/apps/cli/tests/profiles/headless/tests/compaction.e2e.ts index 6e3f46c266..263b794778 100644 --- a/apps/cli/tests/profiles/headless/tests/compaction.e2e.ts +++ b/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. diff --git a/apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts b/apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts index 6816c60f61..74db5cc44a 100644 --- a/apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts +++ b/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) diff --git a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts b/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts index c666871584..557b632132 100644 --- a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts +++ b/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' diff --git a/apps/cli/tests/profiles/headless/tests/resume.e2e.ts b/apps/cli/tests/profiles/headless/tests/resume.e2e.ts index 579b082906..ce8c0bd91d 100644 --- a/apps/cli/tests/profiles/headless/tests/resume.e2e.ts +++ b/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) }) diff --git a/apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts b/apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts index 4ff5fef99c..7a201279f8 100644 --- a/apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts +++ b/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. diff --git a/apps/web/tests/chat-continuous-conversation.e2e.ts b/apps/web/tests/chat-continuous-conversation.e2e.ts index c539a96601..062f5bceac 100644 --- a/apps/web/tests/chat-continuous-conversation.e2e.ts +++ b/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) diff --git a/apps/web/tests/chat-long-interactions.e2e.ts b/apps/web/tests/chat-long-interactions.e2e.ts index 17e639814c..fbc0424c8c 100644 --- a/apps/web/tests/chat-long-interactions.e2e.ts +++ b/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' )) diff --git a/apps/web/tests/chat-scroll-fixture.ts b/apps/web/tests/chat-scroll-fixture.ts index 34ebcc8d1b..bd060c57ff 100644 --- a/apps/web/tests/chat-scroll-fixture.ts +++ b/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') diff --git a/apps/web/tests/clickable-links-gallery.e2e.ts b/apps/web/tests/clickable-links-gallery.e2e.ts index ea3b86dbb5..09423cd4ac 100644 --- a/apps/web/tests/clickable-links-gallery.e2e.ts +++ b/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, diff --git a/apps/web/tests/complex-history.perf.ts b/apps/web/tests/complex-history.perf.ts index faa0146134..bc909b45f5 100644 --- a/apps/web/tests/complex-history.perf.ts +++ b/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') diff --git a/apps/web/tests/feedback-release.e2e.ts b/apps/web/tests/feedback-release.e2e.ts index b63f3c6213..30e12833ee 100644 --- a/apps/web/tests/feedback-release.e2e.ts +++ b/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() diff --git a/apps/web/tests/goal-command-presentation.e2e.ts b/apps/web/tests/goal-command-presentation.e2e.ts index 70ad37a799..157dbf947e 100644 --- a/apps/web/tests/goal-command-presentation.e2e.ts +++ b/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']) diff --git a/apps/web/tests/markdown-cjk-strong.e2e.ts b/apps/web/tests/markdown-cjk-strong.e2e.ts index 237bbd9d3d..40994debcb 100644 --- a/apps/web/tests/markdown-cjk-strong.e2e.ts +++ b/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, diff --git a/apps/web/tests/markdown-images.e2e.ts b/apps/web/tests/markdown-images.e2e.ts index b8f18c6c67..f7f369096b 100644 --- a/apps/web/tests/markdown-images.e2e.ts +++ b/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, diff --git a/apps/web/tests/markdown-inline-code-links.e2e.ts b/apps/web/tests/markdown-inline-code-links.e2e.ts index 62bbf589aa..ac7be4e205 100644 --- a/apps/web/tests/markdown-inline-code-links.e2e.ts +++ b/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, diff --git a/apps/web/tests/markdown-wide-table.e2e.ts b/apps/web/tests/markdown-wide-table.e2e.ts index 09f980ab5c..9607969fef 100644 --- a/apps/web/tests/markdown-wide-table.e2e.ts +++ b/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, diff --git a/apps/web/tests/math-rendering.e2e.ts b/apps/web/tests/math-rendering.e2e.ts index c826d5aff2..1363eb35cf 100644 --- a/apps/web/tests/math-rendering.e2e.ts +++ b/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, diff --git a/apps/web/tests/minimal-preset.snapshot.ts b/apps/web/tests/minimal-preset.snapshot.ts index 22ab1bc476..900cb34055 100644 --- a/apps/web/tests/minimal-preset.snapshot.ts +++ b/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) diff --git a/apps/web/tests/preset-migration.snapshot.ts b/apps/web/tests/preset-migration.snapshot.ts index 7fb3d881d2..a68c5c9f30 100644 --- a/apps/web/tests/preset-migration.snapshot.ts +++ b/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'] : []) diff --git a/apps/web/tests/produced-file-mentions.e2e.ts b/apps/web/tests/produced-file-mentions.e2e.ts index b8ec77c693..91919dad25 100644 --- a/apps/web/tests/produced-file-mentions.e2e.ts +++ b/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, diff --git a/apps/web/tests/produced-files.e2e.ts b/apps/web/tests/produced-files.e2e.ts index 1097ed3b1e..acb267b644 100644 --- a/apps/web/tests/produced-files.e2e.ts +++ b/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, })), diff --git a/apps/web/tests/reference-composer.e2e.ts b/apps/web/tests/reference-composer.e2e.ts index 39c7786296..3870715531 100644 --- a/apps/web/tests/reference-composer.e2e.ts +++ b/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') diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index 4bc734aa4b..ada371450b 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -877,6 +877,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise 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('') diff --git a/apps/web/tests/schedule-after.e2e.ts b/apps/web/tests/schedule-after.e2e.ts index 587a355f4f..fd7a7bf461 100644 --- a/apps/web/tests/schedule-after.e2e.ts +++ b/apps/web/tests/schedule-after.e2e.ts @@ -205,6 +205,7 @@ async function waitForReply( ): Promise> { 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' diff --git a/apps/web/tests/seeded-history.e2e.ts b/apps/web/tests/seeded-history.e2e.ts index 09110c5360..3472c80c57 100644 --- a/apps/web/tests/seeded-history.e2e.ts +++ b/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') ?? [] diff --git a/apps/web/tests/settings-chrome.e2e.ts b/apps/web/tests/settings-chrome.e2e.ts index 8ef2af7048..ed5b270188 100644 --- a/apps/web/tests/settings-chrome.e2e.ts +++ b/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' }], diff --git a/apps/web/tests/subagent-interrupt-ui.e2e.ts b/apps/web/tests/subagent-interrupt-ui.e2e.ts index 11d96c63fd..7fe4b6f2de 100644 --- a/apps/web/tests/subagent-interrupt-ui.e2e.ts +++ b/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 }) diff --git a/apps/web/tests/subagent-interrupt.e2e.ts b/apps/web/tests/subagent-interrupt.e2e.ts index 71e25c5918..9031d5577b 100644 --- a/apps/web/tests/subagent-interrupt.e2e.ts +++ b/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') diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index bfc5221538..f0cecf4512 100644 --- a/docs/config-catalog.i18n.yaml +++ b/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 diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 50626a7d85..0b56a574ca 100644 --- a/docs/config-catalog.md +++ b/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) diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index d2ae35b91a..ac10d61b7c 100644 --- a/docs/config-catalog.zh.md +++ b/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) diff --git a/packages/acp/acp/tests/bridge.spec.ts b/packages/acp/acp/tests/bridge.spec.ts index ad1fa30fac..218a88ada4 100644 --- a/packages/acp/acp/tests/bridge.spec.ts +++ b/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) }) diff --git a/packages/acp/acp/tests/turns.spec.ts b/packages/acp/acp/tests/turns.spec.ts index a17223750f..8b8e38d8ab 100644 --- a/packages/acp/acp/tests/turns.spec.ts +++ b/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' } }) }) diff --git a/packages/api/session-controller/src/commands.ts b/packages/api/session-controller/src/commands.ts index 35fc113408..a564234664 100644 --- a/packages/api/session-controller/src/commands.ts +++ b/packages/api/session-controller/src/commands.ts @@ -541,6 +541,7 @@ export class SessionCommandController { private async readSessionState(sessionId: SessionId): Promise { 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 diff --git a/packages/api/session-controller/src/history.ts b/packages/api/session-controller/src/history.ts index cfaf00421e..a22badc1f1 100644 --- a/packages/api/session-controller/src/history.ts +++ b/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)) diff --git a/packages/api/session-controller/src/index.ts b/packages/api/session-controller/src/index.ts index 9cc93be179..8c4107f592 100644 --- a/packages/api/session-controller/src/index.ts +++ b/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(), }) } diff --git a/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts b/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts index d7559bc047..22a38f92f9 100644 --- a/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts +++ b/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, { diff --git a/packages/api/session-controller/tests/session-cold.host.spec.ts b/packages/api/session-controller/tests/session-cold.host.spec.ts index eaca25062f..54003d6a65 100644 --- a/packages/api/session-controller/tests/session-cold.host.spec.ts +++ b/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({ diff --git a/packages/api/session-controller/tests/session-fork.host.spec.ts b/packages/api/session-controller/tests/session-fork.host.spec.ts index 1661f505bc..355f99c0a6 100644 --- a/packages/api/session-controller/tests/session-fork.host.spec.ts +++ b/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({ diff --git a/packages/api/session-controller/tests/session-projections.host.spec.ts b/packages/api/session-controller/tests/session-projections.host.spec.ts index 90ffb10456..9339df6779 100644 --- a/packages/api/session-controller/tests/session-projections.host.spec.ts +++ b/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) diff --git a/packages/api/session-controller/tests/session-rename.host.spec.ts b/packages/api/session-controller/tests/session-rename.host.spec.ts index 60987608b9..55ed1e32e0 100644 --- a/packages/api/session-controller/tests/session-rename.host.spec.ts +++ b/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' } }) diff --git a/packages/api/session-controller/tests/transport.host.spec.ts b/packages/api/session-controller/tests/transport.host.spec.ts index ebc731769e..9ef70e5d8d 100644 --- a/packages/api/session-controller/tests/transport.host.spec.ts +++ b/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: {} }, diff --git a/packages/bundle/headless/src/index.ts b/packages/bundle/headless/src/index.ts index 50a5c664be..90f29b460a 100644 --- a/packages/bundle/headless/src/index.ts +++ b/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)}`) diff --git a/packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts b/packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts index 0db57e2139..0716dd609f 100644 --- a/packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts +++ b/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]! diff --git a/packages/compaction/command-compact/tests/command-compact.spec.ts b/packages/compaction/command-compact/tests/command-compact.spec.ts index f2ab167d69..92013469ac 100644 --- a/packages/compaction/command-compact/tests/command-compact.spec.ts +++ b/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) diff --git a/packages/compaction/command-compact/tests/loader-composition.spec.ts b/packages/compaction/command-compact/tests/loader-composition.spec.ts index 442d8196dc..5b8402aae7 100644 --- a/packages/compaction/command-compact/tests/loader-composition.spec.ts +++ b/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', diff --git a/packages/compaction/compaction-basic/src/region.ts b/packages/compaction/compaction-basic/src/region.ts index 8bdecee0cf..e8efef2b12 100644 --- a/packages/compaction/compaction-basic/src/region.ts +++ b/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 diff --git a/packages/compaction/compaction-basic/tests/compaction-basic.spec.ts b/packages/compaction/compaction-basic/tests/compaction-basic.spec.ts index bd6a48af66..df5cbce97c 100644 --- a/packages/compaction/compaction-basic/tests/compaction-basic.spec.ts +++ b/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('') expect(head.content.at(-1)).toEqual({ type: 'text', text: '' }) + // 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 = () => Promise.resolve(undefined), ): Promise { 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 diff --git a/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts b/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts index a527226c03..a5834df1c7 100644 --- a/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts +++ b/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' } }, diff --git a/packages/compaction/compaction-basic/tests/manual-compaction.spec.ts b/packages/compaction/compaction-basic/tests/manual-compaction.spec.ts index 9a118cd5a9..daef651acd 100644 --- a/packages/compaction/compaction-basic/tests/manual-compaction.spec.ts +++ b/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() diff --git a/packages/compaction/compaction-tool-result-pruner/src/index.ts b/packages/compaction/compaction-tool-result-pruner/src/index.ts index 18c752d8f1..6d8ef92298 100644 --- a/packages/compaction/compaction-tool-result-pruner/src/index.ts +++ b/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 }) diff --git a/packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts b/packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts index a09d0a3c53..7bcbd0ed62 100644 --- a/packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts +++ b/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) diff --git a/packages/compaction/compaction/src/invariant.ts b/packages/compaction/compaction/src/invariant.ts index ce19a6670f..5682f066db 100644 --- a/packages/compaction/compaction/src/invariant.ts +++ b/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) { diff --git a/packages/compaction/compaction/src/tool-pairing.ts b/packages/compaction/compaction/src/tool-pairing.ts index 56ec6a2368..ca070851ab 100644 --- a/packages/compaction/compaction/src/tool-pairing.ts +++ b/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)`) diff --git a/packages/compaction/compaction/tests/compaction.spec.ts b/packages/compaction/compaction/tests/compaction.spec.ts index 350d4b8308..887362b18b 100644 --- a/packages/compaction/compaction/tests/compaction.spec.ts +++ b/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']) }) diff --git a/packages/compaction/compaction/tests/invariant.spec.ts b/packages/compaction/compaction/tests/invariant.spec.ts index ff49a3400e..314ed70ba1 100644 --- a/packages/compaction/compaction/tests/invariant.spec.ts +++ b/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) diff --git a/packages/compaction/compaction/tests/tool-pairing.spec.ts b/packages/compaction/compaction/tests/tool-pairing.spec.ts index a5694d1514..a3dbb7e88c 100644 --- a/packages/compaction/compaction/tests/tool-pairing.spec.ts +++ b/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 } diff --git a/packages/context/agent-instructions/src/index.ts b/packages/context/agent-instructions/src/index.ts index f614926241..f775c30a9c 100644 --- a/packages/context/agent-instructions/src/index.ts +++ b/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) }) diff --git a/packages/context/agent-instructions/src/state.ts b/packages/context/agent-instructions/src/state.ts index fb25e262f9..8a66f7d02f 100644 --- a/packages/context/agent-instructions/src/state.ts +++ b/packages/context/agent-instructions/src/state.ts @@ -139,6 +139,7 @@ function visibleInstructionChanges( ): Map { const visible = new Map() 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) diff --git a/packages/context/agent-instructions/tests/agent-instructions.e2e.ts b/packages/context/agent-instructions/tests/agent-instructions.e2e.ts index 7e35bdee21..2c256150c8 100644 --- a/packages/context/agent-instructions/tests/agent-instructions.e2e.ts +++ b/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' diff --git a/packages/context/agent-instructions/tests/agent-instructions.spec.ts b/packages/context/agent-instructions/tests/agent-instructions.spec.ts index d477cf666e..259a419bb4 100644 --- a/packages/context/agent-instructions/tests/agent-instructions.spec.ts +++ b/packages/context/agent-instructions/tests/agent-instructions.spec.ts @@ -277,6 +277,7 @@ async function syncedWorkspaceContext(ctx: Context, agent: Agent): Promise 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({ diff --git a/packages/context/session-reference/tests/loader-composition.spec.ts b/packages/context/session-reference/tests/loader-composition.spec.ts index c680e393ca..624b28de33 100644 --- a/packages/context/session-reference/tests/loader-composition.spec.ts +++ b/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) }) diff --git a/packages/context/session-reference/tests/session-reference.spec.ts b/packages/context/session-reference/tests/session-reference.spec.ts index dc2caa5067..cbb14be78d 100644 --- a/packages/context/session-reference/tests/session-reference.spec.ts +++ b/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) }) diff --git a/packages/context/time-context/src/index.ts b/packages/context/time-context/src/index.ts index 02cf81d738..f626342546 100644 --- a/packages/context/time-context/src/index.ts +++ b/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] diff --git a/packages/context/time-context/src/invariant.ts b/packages/context/time-context/src/invariant.ts index 90ec5f2ff2..2fa686a4f4 100644 --- a/packages/context/time-context/src/invariant.ts +++ b/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'] }) diff --git a/packages/context/time-context/tests/time-context.spec.ts b/packages/context/time-context/tests/time-context.spec.ts index 52700e6d62..16e9a52a89 100644 --- a/packages/context/time-context/tests/time-context.spec.ts +++ b/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() diff --git a/packages/context/tmux-context/tests/tmux-context.spec.ts b/packages/context/tmux-context/tests/tmux-context.spec.ts index bc9f9f0ed5..ef1b0e930c 100644 --- a/packages/context/tmux-context/tests/tmux-context.spec.ts +++ b/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 diff --git a/packages/core/agent-loop/src/index.ts b/packages/core/agent-loop/src/index.ts index aa967f87c0..6c30f04135 100644 --- a/packages/core/agent-loop/src/index.ts +++ b/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 { 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 diff --git a/packages/core/agent-loop/src/invariant.ts b/packages/core/agent-loop/src/invariant.ts index b1b2cc93c3..3b1c62d081 100644 --- a/packages/core/agent-loop/src/invariant.ts +++ b/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') diff --git a/packages/core/agent-loop/src/runtime-context.ts b/packages/core/agent-loop/src/runtime-context.ts index daabf5bc19..af9f3fa640 100644 --- a/packages/core/agent-loop/src/runtime-context.ts +++ b/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 diff --git a/packages/core/agent-loop/tests/agent-initiator.spec.ts b/packages/core/agent-loop/tests/agent-initiator.spec.ts index 0b9d32ec06..f25740593a 100644 --- a/packages/core/agent-loop/tests/agent-initiator.spec.ts +++ b/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' })) diff --git a/packages/core/agent-loop/tests/agent.spec.ts b/packages/core/agent-loop/tests/agent.spec.ts index 2ec5bcf01c..1d5ea1ac41 100644 --- a/packages/core/agent-loop/tests/agent.spec.ts +++ b/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) }) diff --git a/packages/core/agent-loop/tests/cancel.spec.ts b/packages/core/agent-loop/tests/cancel.spec.ts index 2b1781083c..00d9339fa6 100644 --- a/packages/core/agent-loop/tests/cancel.spec.ts +++ b/packages/core/agent-loop/tests/cancel.spec.ts @@ -50,6 +50,7 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { /** 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' } }) diff --git a/packages/core/agent-loop/tests/contract-regressions.spec.ts b/packages/core/agent-loop/tests/contract-regressions.spec.ts index 68cb93f129..841bbbd63f 100644 --- a/packages/core/agent-loop/tests/contract-regressions.spec.ts +++ b/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']) diff --git a/packages/core/agent-loop/tests/coverage-edges.spec.ts b/packages/core/agent-loop/tests/coverage-edges.spec.ts index 1fa24977b2..f18e662e61 100644 --- a/packages/core/agent-loop/tests/coverage-edges.spec.ts +++ b/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') }) diff --git a/packages/core/agent-loop/tests/inbox.spec.ts b/packages/core/agent-loop/tests/inbox.spec.ts index 028c42ca5c..021e2339f7 100644 --- a/packages/core/agent-loop/tests/inbox.spec.ts +++ b/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) }) }) diff --git a/packages/core/agent-loop/tests/interception.spec.ts b/packages/core/agent-loop/tests/interception.spec.ts index b50fb45942..3decd9499e 100644 --- a/packages/core/agent-loop/tests/interception.spec.ts +++ b/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() } diff --git a/packages/core/agent-loop/tests/loop.spec.ts b/packages/core/agent-loop/tests/loop.spec.ts index fb98a37e31..68b8683cb0 100644 --- a/packages/core/agent-loop/tests/loop.spec.ts +++ b/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 => 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') }) }) diff --git a/packages/core/agent-loop/tests/properties.spec.ts b/packages/core/agent-loop/tests/properties.spec.ts index 68e170f27e..bf3c723153 100644 --- a/packages/core/agent-loop/tests/properties.spec.ts +++ b/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 diff --git a/packages/core/agent-loop/tests/request-cache.e2e.ts b/packages/core/agent-loop/tests/request-cache.e2e.ts index ceaa2b0b6d..5a1c912bc0 100644 --- a/packages/core/agent-loop/tests/request-cache.e2e.ts +++ b/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) diff --git a/packages/core/agent-loop/tests/request-error.spec.ts b/packages/core/agent-loop/tests/request-error.spec.ts index 616a817082..f0ae238d80 100644 --- a/packages/core/agent-loop/tests/request-error.spec.ts +++ b/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' } }, diff --git a/packages/core/agent-loop/tests/request-freeze.spec.ts b/packages/core/agent-loop/tests/request-freeze.spec.ts index 560be88627..82261178fe 100644 --- a/packages/core/agent-loop/tests/request-freeze.spec.ts +++ b/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' } } }, }) diff --git a/packages/core/agent-loop/tests/request-reconstruction.spec.ts b/packages/core/agent-loop/tests/request-reconstruction.spec.ts index 2450a21999..e5ec05cb60 100644 --- a/packages/core/agent-loop/tests/request-reconstruction.spec.ts +++ b/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([ diff --git a/packages/core/agent-loop/tests/resume.spec.ts b/packages/core/agent-loop/tests/resume.spec.ts index 271849eba3..838c3e7004 100644 --- a/packages/core/agent-loop/tests/resume.spec.ts +++ b/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() diff --git a/packages/core/agent-loop/tests/scope-lifecycle.spec.ts b/packages/core/agent-loop/tests/scope-lifecycle.spec.ts index 81bfc64abd..851701a646 100644 --- a/packages/core/agent-loop/tests/scope-lifecycle.spec.ts +++ b/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() }) diff --git a/packages/core/agent-loop/tests/system-prompt-admission.spec.ts b/packages/core/agent-loop/tests/system-prompt-admission.spec.ts index d3702147f6..e57df3e855 100644 --- a/packages/core/agent-loop/tests/system-prompt-admission.spec.ts +++ b/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() const release = Promise.withResolvers() 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) }) }) diff --git a/packages/core/agent-loop/tests/tool-calls.spec.ts b/packages/core/agent-loop/tests/tool-calls.spec.ts index 74871d4a0d..dc6f783139 100644 --- a/packages/core/agent-loop/tests/tool-calls.spec.ts +++ b/packages/core/agent-loop/tests/tool-calls.spec.ts @@ -42,6 +42,7 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { } function events(agent: Agent): readonly SessionEvent[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() } diff --git a/packages/core/agent-loop/tests/tool-order.spec.ts b/packages/core/agent-loop/tests/tool-order.spec.ts index 287f5fe761..e4c05be289 100644 --- a/packages/core/agent-loop/tests/tool-order.spec.ts +++ b/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) }) }) diff --git a/packages/core/agent/tests/consumed-work.spec.ts b/packages/core/agent/tests/consumed-work.spec.ts index 26808c5c81..221a5f7d0f 100644 --- a/packages/core/agent/tests/consumed-work.spec.ts +++ b/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, }) diff --git a/packages/core/session/src/index.ts b/packages/core/session/src/index.ts index 68577578dd..dbf124893e 100644 --- a/packages/core/session/src/index.ts +++ b/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') diff --git a/packages/core/session/src/invariant.ts b/packages/core/session/src/invariant.ts index 6ed0b6b3c5..be4b214c86 100644 --- a/packages/core/session/src/invariant.ts +++ b/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)) } diff --git a/packages/core/session/tests/canonical-envelopes.spec.ts b/packages/core/session/tests/canonical-envelopes.spec.ts index 123852762d..3177f333d6 100644 --- a/packages/core/session/tests/canonical-envelopes.spec.ts +++ b/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([]) diff --git a/packages/core/session/tests/derived-cache.spec.ts b/packages/core/session/tests/derived-cache.spec.ts index 21d2b7153b..35bf0fa15d 100644 --- a/packages/core/session/tests/derived-cache.spec.ts +++ b/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() } diff --git a/packages/core/session/tests/fork.spec.ts b/packages/core/session/tests/fork.spec.ts index d67538ded8..a62019e23c 100644 --- a/packages/core/session/tests/fork.spec.ts +++ b/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) diff --git a/packages/core/session/tests/invariant.spec.ts b/packages/core/session/tests/invariant.spec.ts index 9b8b281cf5..778e4b560f 100644 --- a/packages/core/session/tests/invariant.spec.ts +++ b/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 })) diff --git a/packages/core/session/tests/properties.spec.ts b/packages/core/session/tests/properties.spec.ts index 07bf37ac02..200a1107f0 100644 --- a/packages/core/session/tests/properties.spec.ts +++ b/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) })) }) diff --git a/packages/core/session/tests/request-header.spec.ts b/packages/core/session/tests/request-header.spec.ts index c7b2719b6e..5aa4a5695b 100644 --- a/packages/core/session/tests/request-header.spec.ts +++ b/packages/core/session/tests/request-header.spec.ts @@ -80,6 +80,7 @@ describe('foldRequestHeader', () => { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) session.append('request/header', { header: { config: { provider: 'mock', model: 'other' } }, reason: 'change' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldRequestHeader(session.snapshotEvents())).toEqual({ config: { provider: 'mock', model: 'other' } }) }) }) diff --git a/packages/core/session/tests/sequence-types.spec.ts b/packages/core/session/tests/sequence-types.spec.ts index d20d2c721f..b6aaee8a16 100644 --- a/packages/core/session/tests/sequence-types.spec.ts +++ b/packages/core/session/tests/sequence-types.spec.ts @@ -69,11 +69,13 @@ describe('Session log positions', () => { isSeeded: true, } + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = Session.create(id, source.snapshotEvents(), header, source.seq) expect(child.header.isSeeded).toBe(true) expect('seedLength' in child.header).toBe(false) expect(child.inheritedEventCount).toBe(2) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.ownEvents().map(event => event.type)).toEqual(['session/end-seed']) expect(child.isOwnSeq(SessionSeq(1))).toBe(false) expect(child.isOwnSeq(SessionSeq(2))).toBe(true) @@ -90,6 +92,7 @@ describe('Session log positions', () => { parent.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) const id = SessionId('suffix-child') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = Session.create(id, parent.snapshotEvents(), { version: SESSION_FORMAT_VERSION, id, @@ -98,6 +101,7 @@ describe('Session log positions', () => { }, parent.seq) child.append('request/context', { provider: 'provider', model: 'model' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.ownEvents().map(event => event.type)).toEqual([ 'session/end-seed', 'request/context', diff --git a/packages/core/session/tests/session.spec.ts b/packages/core/session/tests/session.spec.ts index d6762ba147..ed5dfe46d4 100644 --- a/packages/core/session/tests/session.spec.ts +++ b/packages/core/session/tests/session.spec.ts @@ -71,6 +71,7 @@ describe('Session', () => { session.append('turn/start', { turn: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'max-tokens' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEnd = session.snapshotEvents().findLast(e => e.type === 'turn/end')! expect(turnEnd.data.reason).toEqual({ kind: 'max-tokens' }) // survives a structuredClone (the persistence-serialization boundary) @@ -81,8 +82,11 @@ describe('Session', () => { const session = Session.create(SessionId('aborted')) session.append('turn/start', { turn: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'aborted', reason: { kind: 'user' } } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('aborted-replay'), structuredClone(session.snapshotEvents())) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayed.snapshotEvents().slice(0, -1)).toEqual(session.snapshotEvents()) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEnd = replayed.snapshotEvents().findLast(event => event.type === 'turn/end') expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason) .toEqual({ kind: 'aborted', reason: { kind: 'user' } }) @@ -115,6 +119,7 @@ describe('Session', () => { session.append('user/message', message, { surfaceOp: 'append' }) expect(session.deriveMessages()).toEqual([message]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents()[0] expect(event?.type === 'user/message' && event.data.source).toEqual({ kind: 'plugin', plugin: 'agent-instructions' }) }) @@ -139,9 +144,11 @@ describe('Session', () => { }, { surfaceOp: 'append' }) original.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('s3-replay'), original.snapshotEvents()) expect(replayed.deriveMessages()).toEqual(original.deriveMessages()) // The seed verbatim, plus the end-seed event the constructor appends. + // 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) expect(replayed.firstLiveSeq).toBe(original.seq) @@ -149,16 +156,20 @@ describe('Session', () => { it('marks an explicitly empty seed without marking a fresh session', () => { const fresh = Session.create(SessionId('fresh-empty')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(fresh.snapshotEvents()).toEqual([]) const resumed = Session.create(SessionId('resumed-empty'), []) expect(resumed.firstLiveSeq).toBe(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents()).toMatchObject([ { type: 'session/end-seed', seq: 0, data: {} }, ]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const reopened = Session.create(SessionId('reopened-empty'), resumed.snapshotEvents()) expect(reopened.firstLiveSeq).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(reopened.snapshotEvents()).toEqual(resumed.snapshotEvents()) }) @@ -188,6 +199,7 @@ describe('Session', () => { const unrelatedPrimitiveData = { type: 'plugin/event', seq: 0, time: 1, data: null, } as unknown as SessionEvent + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(Session.create(SessionId('primitive-plugin-data'), [unrelatedPrimitiveData]).snapshotEvents().slice(0, 1)) .toEqual([unrelatedPrimitiveData]) }) @@ -250,6 +262,7 @@ describe('Session', () => { SessionLogOffset(0), 'detached', ) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(restored.eventAt(SessionSeq(0))).toBe(mismatchedMessage) expect(Object.isFrozen(mismatchedMessage)).toBe(false) }) @@ -519,6 +532,7 @@ describe('Session', () => { reason: 'initial', }, } as const + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(Session.create(SessionId('reasoning-effort'), [valid]).snapshotEvents()[0]) .toEqual(valid) @@ -549,6 +563,7 @@ describe('Session', () => { reason: 'initial', }, } as const + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(Session.create(SessionId('adapter-defaults'), [valid]).snapshotEvents()[0]).toEqual(valid) for (const adapterDefaults of [ @@ -579,6 +594,7 @@ describe('Session', () => { isError: false, }), }, { surfaceOp: 'append' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = structuredClone(session.snapshotEvents()) // A misbehaving consumer tries to mutate the messages it was handed. @@ -595,6 +611,7 @@ describe('Session', () => { messages.reverse() // The log is unchanged: deep-equal to the snapshot taken before mutation. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual(before) // And a fresh derivation still reflects the original content and order. expect(session.deriveMessages()[0]!.content).toEqual([{ type: 'text', text: 'original' }]) @@ -624,6 +641,7 @@ describe('Session', () => { cyclic['self'] = cyclic expect(bad(cyclic)).toThrow(/non-JSON-serializable/) // The rejected appends never entered the log. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(0) }) @@ -638,12 +656,14 @@ describe('Session', () => { }))) .toThrow(/surface-eligible and requires a surfaceOp marker/) // The rejected append never entered the log (only turn/start is present). + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(1) }) it('accepts dense arrays and nested plain objects', () => { const session = Session.create(SessionId('s6')) expect(() => session.append('user/message', { content: [{ type: 'text', text: 'x' }], source: { kind: 'user' }, extra: [1, 2, [3, { a: null, b: true }]] } as never, { surfaceOp: 'append' })).not.toThrow() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(1) }) @@ -688,6 +708,7 @@ describe('Session', () => { { type: 'turn/end' as const, seq: 2, time: 3, data: { turn: 1, reason: { kind: 'completed' as const } } }, ] as SessionEvent[] const session = Session.create(SessionId('seed-ok'), goodSeed) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().slice(0, 3)).toEqual(goodSeed) expect(session.firstLiveSeq).toBe(3) }) @@ -713,6 +734,7 @@ describe('Session', () => { const session = Session.create(SessionId('seed-entry-snapshot'), seed) expect(reads).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().slice(0, 1)).toEqual([accepted]) }) @@ -730,6 +752,7 @@ describe('Session', () => { const session = Session.create(SessionId('seed-nested-drift'), seed) expect(reads).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()[0]!.data).toEqual({ value: 'accepted' }) }) @@ -791,6 +814,7 @@ describe('Session', () => { const session = Session.create(SessionId('seed-null-prototype'), [event]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().slice(0, 1)).toEqual([{ ...event }]) }) @@ -823,6 +847,7 @@ describe('Session', () => { }] as unknown as SessionEvent[] const session = Session.create(SessionId('seed-unstable-metadata'), seed) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents()[1]! if (event.type !== 'user/message') throw new Error('test fixture must remain a user/message') @@ -883,6 +908,7 @@ describe('Session', () => { const um = seed[1]! ;(um.data as { content: { type: 'text'; text: string }[] }).content[0]!.text = 'HACKED' ;(um.data as Record)['injected'] = 1n // would have failed validation + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[1]! expect(logged.type === 'user/message' && (logged.data.content[0] as { text: string }).text).toBe('original') expect((logged.data as Record)['injected']).toBeUndefined() @@ -901,6 +927,7 @@ describe('Session', () => { // make session.snapshotEvents() diverge from the value that passed validation. data.content[0]!.text = 'HACKED' ;(data as Record)['injected'] = 1n + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[0]! expect(logged.type === 'user/message' && (logged.data.content[0] as { text: string }).text).toBe('original') expect((logged.data as Record)['injected']).toBeUndefined() @@ -923,6 +950,7 @@ describe('Session', () => { expect(reads).toBe(1) expect(event.data).toEqual({ value: 'accepted' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([event]) }) @@ -936,6 +964,7 @@ describe('Session', () => { }), { surfaceOp: { op: 'replace', startSeq: 1n, endSeq: 2 } } as never, )).toThrow(/non-JSON-serializable surface metadata/) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -954,6 +983,7 @@ describe('Session', () => { }), { surfaceOp: new ReplaceOp() }, )).toThrow(/non-JSON-serializable surface metadata/) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -985,6 +1015,7 @@ describe('Session', () => { expect(reads).toBe(1) expect(event.surfaceOp).toEqual({ op: 'replace', startSeq: 0, endSeq: 0 }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([source, event]) }) @@ -1006,6 +1037,7 @@ describe('Session', () => { surfaceOp: 'append', sourceEventSeqs: [0, -1], })).toThrow(/non-negative safe integers/) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -1029,6 +1061,7 @@ describe('Session', () => { data: { turn: 1 }, surfaceOp: 'append', } as unknown as SessionEvent])).toThrow(/invalid seed event.*not surface-eligible/) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -1039,6 +1072,7 @@ describe('Session', () => { time: 1, data: { turn: 1 }, }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seededEvent = seeded.snapshotEvents()[0]! if (seededEvent.type !== 'turn/start') throw new Error('test fixture must remain a turn/start') expect(Object.isFrozen(seededEvent)).toBe(true) @@ -1059,20 +1093,24 @@ describe('Session', () => { it('returns cached frozen event-array snapshots that do not grow after append', () => { const session = Session.create(SessionId('events-snapshot')) session.append('turn/start', { turn: 1 }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents() const beforeEvent = before[0]! if (beforeEvent.type !== 'turn/start') throw new Error('test fixture must remain a turn/start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toBe(before) expect(Object.isFrozen(before)).toBe(true) expect(() => { (before as SessionEvent[]).push(beforeEvent) }).toThrow(TypeError) expect(() => { beforeEvent.data.turn = 99 }).toThrow(TypeError) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const after = session.snapshotEvents() expect(before).toHaveLength(1) expect(after).toHaveLength(2) expect(after).not.toBe(before) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toBe(after) }) @@ -1081,10 +1119,14 @@ describe('Session', () => { const start = session.append('turn/start', { turn: 1 }) const end = session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(0))).toBe(start) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(1))).toBe(end) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(2))).toBeUndefined() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const range = session.snapshotEvents(SessionLogOffset(1), SessionLogOffset(2)) expect(range).toEqual([end]) expect(Object.isFrozen(range)).toBe(true) @@ -1230,6 +1272,7 @@ describe('Session', () => { const marked = Session.create(SessionId('ignorable-envelope'), [ { ...base, ignorable: true } as SessionEvent, ]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(marked.snapshotEvents()[0]?.ignorable).toBe(true) }) }) @@ -1274,6 +1317,7 @@ describe('SessionStore', () => { a.append('user/message', createUserMessage({ content: [{ type: 'text', text: 'q' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const forked = ctx.sessions.create(SessionId('fork'), { seed: a.snapshotEvents() }) expect(forked.deriveMessages()).toEqual(a.deriveMessages()) }) @@ -1537,6 +1581,7 @@ describe('SessionStore', () => { const heard: SessionEvent[] = [] let committedBeforeNotify = false ctx.on('session/event', (observedSession, event) => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. committedBeforeNotify = observedSession.snapshotEvents().at(-1) === event throw new Error('sync event observer') }) @@ -1550,6 +1595,7 @@ describe('SessionStore', () => { }) }).not.toThrow() expect(committedBeforeNotify).toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(heard).toEqual([appended]) await Promise.resolve() @@ -1573,6 +1619,7 @@ describe('SessionStore', () => { const [observedSession, event] = args as [Session, SessionEvent] validations.push({ event, + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. logLength: observedSession.snapshotEvents().length, frozen: Object.isFrozen(event) && Object.isFrozen(event.data), }) @@ -1586,6 +1633,7 @@ describe('SessionStore', () => { expect(() => session.append('turn/start', { turn: 1, })).toThrow('reject first candidate') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) expect(observed).toEqual([]) @@ -1598,6 +1646,7 @@ describe('SessionStore', () => { ]) expect(validations.map(({ event }) => event.seq)).toEqual([0, 0]) expect(validations[1]!.event).toBe(appended) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(observed).toEqual([appended]) }) @@ -1629,6 +1678,7 @@ describe('SessionStore', () => { sourceEventSeqs: [SessionSeq(2)], })).toThrow('reject surface candidate') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(3) expect(surface.nodes).toEqual([2]) expect(surface.replaceGeneration).toBe(0) @@ -1654,6 +1704,7 @@ describe('SessionStore', () => { expect(() => session.append('turn/start', { turn: 1, })).toThrow('dispatch instrumentation rejected the carrier') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) expect(observed).toEqual([]) }) @@ -1673,6 +1724,7 @@ describe('SessionStore', () => { const appended = session.append('turn/start', { turn: 1, }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(heard).toEqual([appended]) expect(warnings).toEqual([ @@ -1704,6 +1756,7 @@ describe('SessionStore', () => { turn: 1, }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(order).toEqual(['resolve:live', 'observe:live', 'dispose:detached']) expect(ctx.sessions.get(session.id)).toBeUndefined() diff --git a/packages/core/session/tests/surface.spec.ts b/packages/core/session/tests/surface.spec.ts index 6d10e22335..5436777e22 100644 --- a/packages/core/session/tests/surface.spec.ts +++ b/packages/core/session/tests/surface.spec.ts @@ -328,6 +328,7 @@ describe('SurfaceManager', () => { sourceEventSeqs: sourceSeqs(2, 1), }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const folded = foldSurface(s.snapshotEvents()) expect(folded.nodes).toEqual(s.surface.nodes) expect(folded.replacements).toEqual([ @@ -337,7 +338,9 @@ describe('SurfaceManager', () => { folded.nodes[0] = SessionSeq(99) folded.replacements[0]!.shadowedSeqs.push(SessionSeq(99)) expect(s.surface.nodes).toEqual([3]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(s.snapshotEvents()).nodes).toEqual([3]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(s.snapshotEvents()).replacements[0]!.shadowedSeqs).toEqual([0]) }) @@ -354,6 +357,7 @@ describe('SurfaceManager', () => { expect(s.surface.nodes).toEqual([1]) const manager = s.surface as unknown as { _state: object } expect(Object.hasOwn(manager._state, 'replacements')).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(s.snapshotEvents()).replacements).toEqual([ { seq: 1, start: 0, end: 0, shadowedSeqs: [0] }, ]) @@ -388,6 +392,7 @@ describe('SurfaceManager', () => { const surface = s.surface const nodes = surface.nodes + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(nodes).toEqual(foldSurface(s.snapshotEvents()).nodes) expect(surface.replaceGeneration).toBe(0) @@ -408,10 +413,12 @@ describe('SurfaceManager', () => { { surfaceOp: surfaceOp({ op: 'replace', startSeq: 0, endSeq: 0 }) }, )).toThrow(/missing 0/) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(s.snapshotEvents()).toHaveLength(1) expect(s.surface).toBe(surface) expect(surface.nodes).toEqual([0]) expect(surface.replaceGeneration).toBe(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(surface.nodes).toEqual(foldSurface(s.snapshotEvents()).nodes) s.append('user/message', createUserMessage({ @@ -420,6 +427,7 @@ describe('SurfaceManager', () => { expect(surface.nodes).toBe(nodes) expect(surface.nodes).toEqual([0, 1]) expect(surface.replaceGeneration).toBe(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(surface.nodes).toEqual(foldSurface(s.snapshotEvents()).nodes) }) @@ -493,6 +501,7 @@ describe('SurfaceManager', () => { isError: false, }), }, { surfaceOp: 'append' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('replay'), original.snapshotEvents()) expect(replayed.surface.nodes).toEqual([1, 2, 4]) expect(replayed.deriveMessages()).toEqual(original.deriveMessages()) @@ -594,6 +603,7 @@ describe('SurfaceManager', () => { // Mutate caller's array after append. sources.push(SessionSeq(1)) sources[0] = SessionSeq(99) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = s.snapshotEvents()[1]! as SurfaceEvent expect(logged.sourceEventSeqs).toEqual([0]) }) @@ -629,6 +639,7 @@ describe('SurfaceManager', () => { }) // Mutate caller's object after append. op.startSeq = SessionSeq(99) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = s.snapshotEvents()[1]! as SurfaceEvent expect(logged.surfaceOp).toEqual({ op: 'replace', startSeq: 0, endSeq: 0 }) }) @@ -719,7 +730,9 @@ describe('Session.append surface opts', () => { expect(event.sourceEventSeqs).toEqual([0, 1]) expect(event.surfaceOp).toBe('append') // The logged event matches the returned event. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[2]! as SurfaceEvent).sourceEventSeqs).toEqual([0, 1]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[2]! as SurfaceEvent).surfaceOp).toBe('append') }) @@ -753,7 +766,9 @@ describe('Session.append surface opts', () => { it('a non-surface event carries no surface fields', () => { const s = Session.create(SessionId('noopts')) s.append('turn/start', { turn: 1 }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[0] as SessionEvent).sourceEventSeqs).toBeUndefined() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[0] as SessionEvent).surfaceOp).toBeUndefined() }) @@ -806,12 +821,14 @@ describe('surface type guards', () => { it('isSurfaceEvent narrows a fully-formed surface event', () => { const s = surfaceSession() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const userMessage = s.snapshotEvents().find(e => e.type === 'user/message')! expect(isSurfaceEvent(userMessage)).toBe(true) }) it('isSurfaceEvent rejects a non-surface-eligible type', () => { const s = surfaceSession() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnStart = s.snapshotEvents().find(e => e.type === 'turn/start')! expect(isSurfaceEvent(turnStart)).toBe(false) }) @@ -835,7 +852,9 @@ describe('surface type guards', () => { s.append('user/message', createUserMessage({ content: [{ type: 'text', text: 'checkpoint' }], source: { kind: 'plugin', plugin: 'compact' }, }), { surfaceOp: surfaceOp({ op: 'replace', startSeq: 1, endSeq: 2 }), sourceEventSeqs: sourceSeqs(1, 2) }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const appended = s.snapshotEvents().find(e => e.type === 'user/message')! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replacement = s.snapshotEvents().at(-1)! expect(isAppendSurfaceEvent(appended)).toBe(true) @@ -846,6 +865,7 @@ describe('surface type guards', () => { it('rejects log-only and markerless events from both marker guards', () => { const s = surfaceSession() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnStart = s.snapshotEvents().find(e => e.type === 'turn/start')! // A surface-eligible type whose mandatory marker is absent has no origin at // all: it never entered the surface. diff --git a/packages/core/tools/src/invariant.ts b/packages/core/tools/src/invariant.ts index 2fb5b5878c..d5edf99697 100644 --- a/packages/core/tools/src/invariant.ts +++ b/packages/core/tools/src/invariant.ts @@ -58,6 +58,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant const seed = (session: Session): number | null => { let openTurn: number | null = null dispatchRoots.set(session, new Map()) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { validateDispatch(session, event) commitDispatch(session, event) diff --git a/packages/core/tools/tests/invariant.spec.ts b/packages/core/tools/tests/invariant.spec.ts index 202963379e..795bef2ad8 100644 --- a/packages/core/tools/tests/invariant.spec.ts +++ b/packages/core/tools/tests/invariant.spec.ts @@ -152,6 +152,7 @@ describe('tool-pipeline invariants', () => { name: 'echo', arguments: {}, })).toThrow(/parentCallId child does not belong to rootCallId another-root/) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().some(event => event.type === 'tool/ptc-dispatch-start' && String(event.data.subCallId) === 'invalid-grandchild')).toBe(false) }) diff --git a/packages/core/tools/tests/ptc.spec.ts b/packages/core/tools/tests/ptc.spec.ts index 11bf14e01a..2f2dfebd1a 100644 --- a/packages/core/tools/tests/ptc.spec.ts +++ b/packages/core/tools/tests/ptc.spec.ts @@ -1484,6 +1484,7 @@ describe('the run_code dispatch bridge', () => { expect(result.isError).toBe(false) expect(result.isError ? undefined : result.value).toEqual({ logs: [], result: depth }) expect({ observedDepth, observedLeaf }).toEqual({ observedDepth: depth, observedLeaf: 'leaf' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const dispatch = session.snapshotEvents().find(event => event.type === 'tool/ptc-dispatch') if (dispatch === undefined) throw new Error('expected a durable tool/ptc-dispatch event') const logged = dispatch.data.arguments as { nested: JsonValue } diff --git a/packages/experimental/agent-team/src/mailbox.ts b/packages/experimental/agent-team/src/mailbox.ts index acd842308a..6afc7f441b 100644 --- a/packages/experimental/agent-team/src/mailbox.ts +++ b/packages/experimental/agent-team/src/mailbox.ts @@ -299,6 +299,7 @@ export class TeamMailbox { /** Whether a target Session already contains the durable message identity. */ private targetRecorded(session: Session, messageId: TeamMessageId): boolean { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const suffix = session.snapshotEvents(session.inheritedEventCount) return messageAccepted(suffix, message => message.source.kind === 'team-message' && message.source.messageId === messageId) diff --git a/packages/experimental/agent-team/src/roster.ts b/packages/experimental/agent-team/src/roster.ts index 3d0ca8372e..ae52c741c2 100644 --- a/packages/experimental/agent-team/src/roster.ts +++ b/packages/experimental/agent-team/src/roster.ts @@ -375,6 +375,7 @@ export class TeamRoster { try { signal.throwIfAborted() await this.ctx.sessions.flush(session) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const suffix = session.snapshotEvents(session.inheritedEventCount) if (messageAccepted(suffix, message => message.id === messageId)) return if (this.ctx.sessions.get(childId) !== session) continue @@ -482,6 +483,7 @@ export class TeamRoster { /** Whether a Session's own suffix identifies a provider-owned subagent child. */ private subagentDescriptor(agent: Agent): boolean { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return foldSubagentDescriptor(agent.session.snapshotEvents(agent.session.inheritedEventCount)) !== undefined } } diff --git a/packages/experimental/agent-team/tests/invariant.spec.ts b/packages/experimental/agent-team/tests/invariant.spec.ts index 5dd8dba256..df9ec3b3a7 100644 --- a/packages/experimental/agent-team/tests/invariant.spec.ts +++ b/packages/experimental/agent-team/tests/invariant.spec.ts @@ -44,6 +44,7 @@ describe('Agent Teams stream invariant', () => { code: 'INVARIANT', packageName: '@deepseek-ai/dsh-experimental-agent-team', })) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(invalid.snapshotEvents()).toEqual([]) }) @@ -69,6 +70,7 @@ describe('Agent Teams stream invariant', () => { code: 'INVARIANT', packageName: '@deepseek-ai/dsh-experimental-agent-team', })) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) }) diff --git a/packages/experimental/agent-team/tests/persistence.spec.ts b/packages/experimental/agent-team/tests/persistence.spec.ts index 5237fc0301..c7d4414792 100644 --- a/packages/experimental/agent-team/tests/persistence.spec.ts +++ b/packages/experimental/agent-team/tests/persistence.spec.ts @@ -31,6 +31,7 @@ function durable(agent: Agent): { pendingMessages: TeamMessageSnapshot[] } { let projected = teamProjectionDefinition.init(agent.session.header) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of agent.session.snapshotEvents()) projected = teamProjectionDefinition.apply(projected, event) if (projected.failure !== undefined) throw new Error(projected.failure) const state = projected @@ -158,6 +159,7 @@ async function persistedChild( // Live sessions persist only through an attached agent-loop writer; this // bare fixture session seeds its durable log directly for the cold restart. const handle = await ctx.sessionPersistence.create(child.header) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await handle.append(child.snapshotEvents()) await handle.close() return child @@ -393,6 +395,7 @@ for (const backend of backends) { const checkpointEntered = Promise.withResolvers() const releaseCheckpoint = Promise.withResolvers() const delayedCheckpoint = vi.spyOn(second.ctx.sessions, 'flush').mockImplementation(async (session) => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. if (session.id === rootId && session.snapshotEvents().some(event => event.type === 'team/message/delivered' && event.data.messageId === messageId)) { checkpointEntered.resolve(undefined) diff --git a/packages/experimental/agent-team/tests/team.spec.ts b/packages/experimental/agent-team/tests/team.spec.ts index 79354a6427..d19f1522b7 100644 --- a/packages/experimental/agent-team/tests/team.spec.ts +++ b/packages/experimental/agent-team/tests/team.spec.ts @@ -35,6 +35,7 @@ function durable(agent: Agent): { pendingMessages: TeamMessageSnapshot[] } { let projected = teamProjectionDefinition.init(agent.session.header) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of agent.session.snapshotEvents()) projected = teamProjectionDefinition.apply(projected, event) if (projected.failure !== undefined) throw new Error(projected.failure) const state = projected @@ -270,6 +271,7 @@ describe('Team identity and provisioning', () => { // Live sessions persist only through an attached agent-loop writer; this // bare fixture session seeds its durable log directly for the cold reread. const persisted = await ctx.sessionPersistence.create(liveSession.header) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await persisted.append(liveSession.snapshotEvents()) await persisted.close() await liveFiber.dispose() @@ -469,6 +471,7 @@ describe('Team identity and provisioning', () => { await ctx.agentTeams.createTask(lead, { subject: 'parent task', description: 'belongs to parent' }) const handle = await ctx.agents.create({ sessionId: SessionId('ordinary-fork'), + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: lead.session.snapshotEvents(), meta: { parentSession: lead.id, isSeeded: true }, inheritedEventCount: SessionLogOffset(lead.session.seq), @@ -951,6 +954,7 @@ describe('Team mailbox and waiting', () => { }) await expect(teamInternals(ctx).mailbox.tryDispatch(lead, message, SIGNAL)).resolves.toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(lead.session.snapshotEvents().some(event => event.type === 'agent/inbox/spliced' && event.data.inserted.some(input => input.source.kind === 'team-message' && input.source.messageId === message.id))).toBe(true) @@ -995,12 +999,14 @@ describe('Team mailbox and waiting', () => { 'team/message/delivered', ]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const receiptCount = lead.session.snapshotEvents().filter(event => event.type === 'agent/inbox/spliced' && event.data.inserted.some(message => message.source.kind === 'team-message' && messageIds.has(message.source.messageId))).length await teamFiber.dispose() await ctx.plugin(TeamService, { maxPendingMessagesPerMember: 1 }) await vi.waitFor(() => { expect(durable(lead).pendingMessages).toEqual([]) }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(lead.session.snapshotEvents().filter(event => event.type === 'agent/inbox/spliced' && event.data.inserted.some(message => message.source.kind === 'team-message' && messageIds.has(message.source.messageId)))).toHaveLength(receiptCount) @@ -1202,6 +1208,7 @@ describe('Team mailbox and waiting', () => { expect(later.status).toBe('accepted') const target = await waitRunning(ctx, started.member.id) await vi.waitFor(() => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const accepted = target.session.snapshotEvents().flatMap(event => event.type === 'agent/inbox/spliced' ? event.data.inserted.flatMap(message => message.source.kind === 'team-message' ? [message.source.messageId] diff --git a/packages/experimental/agent-team/tests/test-session-query.ts b/packages/experimental/agent-team/tests/test-session-query.ts index 233c493ad2..3b7554873a 100644 --- a/packages/experimental/agent-team/tests/test-session-query.ts +++ b/packages/experimental/agent-team/tests/test-session-query.ts @@ -33,6 +33,7 @@ export class TestSessionQuery extends SessionQueryEngine { options: SessionObservationOptions = {}, ): Promise { const live = this.ctx.sessions.get(sessionId) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. if (live !== undefined) return cut('live', live.header, live.snapshotEvents()) const handle = await this.ctx.sessionPersistence.open( sessionId, diff --git a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts index 6ae2fddca3..74a8175fcd 100644 --- a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts +++ b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts @@ -148,6 +148,7 @@ describe('dsh-tool-team', () => { expect(childAssembly.tools.map(schema => schema.name).filter(name => TOOL_NAMES.includes(name)).sort()) .toEqual(TOOL_NAMES) expect(renderPrompt(childAssembly)).toContain('Your Team role is teammate; your Team name is tool-worker') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const initialPrompt = child.session.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'user') expect(initialPrompt?.type === 'user/message' diff --git a/packages/feedback/command-feedback/tests/command-feedback.spec.ts b/packages/feedback/command-feedback/tests/command-feedback.spec.ts index e59020bfc6..40ef1755b4 100644 --- a/packages/feedback/command-feedback/tests/command-feedback.spec.ts +++ b/packages/feedback/command-feedback/tests/command-feedback.spec.ts @@ -73,6 +73,7 @@ async function run(test: Harness, suffix = ''): Promise<{ kind: string; text?: s /** Authoritative feedback payloads in log order. */ function feedbackTexts(session: Session): string[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents() .filter(event => event.type === 'feedback/record') .map(event => event.data.text) @@ -107,14 +108,17 @@ describe('/feedback human command', () => { text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}.`, }) expect(feedbackTexts(test.session)).toEqual(['the diff view is unreadable']) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const commandRun = test.session.snapshotEvents().find(event => event.type === 'command/run') expect(commandRun?.type === 'command/run' && Object.hasOwn(commandRun.data, 'args')).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(JSON.stringify(test.session.snapshotEvents()).match(/the diff view is unreadable/gu)).toHaveLength(1) }) it('exports a command-independent feedback producer', async () => { const test = await harness() commandFeedback.recordFeedback(test.session, ' recorded outside a command ') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.session.snapshotEvents().map(event => event.type)).toEqual(['feedback/record']) expect(feedbackTexts(test.session)).toEqual(['recorded outside a command']) expect(() => { commandFeedback.recordFeedback(test.session, ' \n\t ') }) @@ -125,6 +129,7 @@ describe('/feedback human command', () => { it('keeps command bookkeeping around the authoritative feedback event', async () => { const test = await harness() await run(test, ' nothing else happens') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.session.snapshotEvents().map(event => event.type)).toEqual([ 'command/run', 'feedback/record', 'command/done', ]) @@ -161,10 +166,12 @@ describe('/feedback human command', () => { it('keeps every recorded event out of model context and derived history', async () => { const test = await harness() await run(test, ' invisible to the model') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of test.session.snapshotEvents()) { expect('surfaceOp' in event).toBe(false) expect(test.session.deriveEventMessage(event)).toBeNull() } + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(test.session.snapshotEvents()).nodes).toEqual([]) expect(test.session.surface.nodes).toEqual([]) expect(test.session.deriveMessages()).toEqual([]) @@ -180,8 +187,10 @@ describe('/feedback human command', () => { await expect(run(test, ' \n\t ')).resolves.toEqual(expected) expect(getOrCreateAnonymousUserId).not.toHaveBeenCalled() expect(feedbackTexts(test.session)).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const done = test.session.snapshotEvents().filter(event => event.type === 'command/done') expect(done.map(event => event.data.kind)).toEqual(['error', 'error']) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of test.session.snapshotEvents()) { if (event.type === 'command/run') expect(Object.hasOwn(event.data, 'args')).toBe(false) } @@ -193,6 +202,7 @@ describe('/feedback human command', () => { controller.abort(new Error('user cancelled the command')) await expect(test.ctx.commands.execute(test.agent, '/feedback too late', [], controller.signal)) .rejects.toThrow('user cancelled the command') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.session.snapshotEvents()).toEqual([]) }) }) diff --git a/packages/feedback/command-feedback/tests/loader-composition.spec.ts b/packages/feedback/command-feedback/tests/loader-composition.spec.ts index 6ab826e516..6867625302 100644 --- a/packages/feedback/command-feedback/tests/loader-composition.spec.ts +++ b/packages/feedback/command-feedback/tests/loader-composition.spec.ts @@ -102,12 +102,16 @@ describe('/feedback real Loader composition through cordis.yml', () => { }) // The domain event owns the payload; generic command bookkeeping omits it. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().map(event => event.type)) .toEqual(['command/run', 'feedback/record', 'command/done', 'command/run', 'command/done']) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const run = owner.session.snapshotEvents().find(event => event.type === 'command/run') expect(run?.type === 'command/run' && Object.hasOwn(run.data, 'args')).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const feedback = owner.session.snapshotEvents().find(event => event.type === 'feedback/record') expect(feedback?.type === 'feedback/record' && feedback.data.text).toBe('the diff view is unreadable') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(JSON.stringify(owner.session.snapshotEvents()).match(/the diff view is unreadable/gu)).toHaveLength(1) // Nothing reached the model. diff --git a/packages/feedback/message-feedback/src/index.ts b/packages/feedback/message-feedback/src/index.ts index 7530ccaa70..2b2fc5ccee 100644 --- a/packages/feedback/message-feedback/src/index.ts +++ b/packages/feedback/message-feedback/src/index.ts @@ -226,10 +226,12 @@ export class MessageFeedbackService extends TypertRemoteService { } const live = this.ctx.sessions.get(sessionId) if (live !== undefined) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return operation(live.snapshotEvents(), async (event) => { if (event !== undefined) { live.append(event.type, event.data) } + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const last = live.snapshotEvents().at(-1) if (!(await this.ctx.sessions.flush(live))) { throw new Error( diff --git a/packages/feedback/message-feedback/tests/helpers.ts b/packages/feedback/message-feedback/tests/helpers.ts index b2ac2f1943..8f3f6c3bb8 100644 --- a/packages/feedback/message-feedback/tests/helpers.ts +++ b/packages/feedback/message-feedback/tests/helpers.ts @@ -186,6 +186,7 @@ class TestPersistence extends SessionPersistence { } persist(session: Session): void { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. this.durable.set(session.id, { meta: session.header, events: [...session.snapshotEvents()] }) } diff --git a/packages/feedback/message-feedback/tests/message-feedback.spec.ts b/packages/feedback/message-feedback/tests/message-feedback.spec.ts index 7a94fefca2..039c0b1388 100644 --- a/packages/feedback/message-feedback/tests/message-feedback.spec.ts +++ b/packages/feedback/message-feedback/tests/message-feedback.spec.ts @@ -62,6 +62,7 @@ describe('MessageFeedbackService public contract', () => { }) const fixture = messageFixture('corrupt-session') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. persistence.setDurable({ meta: fixture.session.header, events: fixture.session.snapshotEvents() }) const corruption = new Error('stored log checksum mismatch') persistence.readFailure = corruption @@ -414,6 +415,7 @@ describe('MessageFeedbackService item concurrency', () => { const replacement = Session.create( old.session.id, + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. old.session.snapshotEvents(), { ...old.session.header, createdAt: 20, cwd: '/new' }, ) @@ -493,6 +495,7 @@ describe('canonical message feedback history', () => { const sessionId = fixture.session.id const messageId = fixture.assistantMessageIds[0] persistence.persist(fixture.session) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const prefix = fixture.session.snapshotEvents() const lifecycle: string[] = [] ctx.on('session/created', () => { lifecycle.push('created') }) @@ -538,6 +541,7 @@ describe('canonical message feedback history', () => { const { ctx, persistence } = await harness() const session = ctx.sessions.create(SessionId('live-log')) const fixture = appendMessageFixture(session) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents().length const failure = new Error('disk unavailable') let fail = true @@ -552,7 +556,9 @@ describe('canonical message feedback history', () => { const item = listed.value.items[0]! fail = false expectItem(await ctx.messageFeedback.put({ ...request, ifVersion: item.version })) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(before + 1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(persistence.durable.get(session.id)?.events).toEqual(session.snapshotEvents()) expect(persistence.openCalls).toEqual(['read']) }) @@ -563,6 +569,7 @@ describe('canonical message feedback history', () => { const session = ctx.sessions.create(SessionId('mismatched-checkpoint')) const fixture = appendMessageFixture(session) ctx.on('session/flush', () => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = [...session.snapshotEvents()] if (kind === 'missing-tail') events.pop() if (kind === 'different-tail') events[events.length - 1] = { ...events.at(-1)!, time: 0 } @@ -587,6 +594,7 @@ describe('canonical message feedback history', () => { expectItem(await ctx.messageFeedback.put({ sessionId: session.id, messageId: fixture.assistantMessageIds[0], rating: 'positive', ifVersion: null, })) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(persistence.durable.get(session.id)!.events.length).toBe(session.snapshotEvents().length - 1) }) @@ -624,6 +632,7 @@ describe('canonical message feedback history', () => { ])('rejects malformed durable feedback payload %#', async (record) => { const { ctx, persistence } = await harness() const fixture = messageFixture('invalid-feedback') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = fixture.session.snapshotEvents() persistence.setDurable({ meta: fixture.session.header, events: [...events, { ...record, seq: SessionSeq(events.length), time: 1, diff --git a/packages/fs/tool-fs/tests/fs-tools.e2e.ts b/packages/fs/tool-fs/tests/fs-tools.e2e.ts index 9e15589025..79bddaa090 100644 --- a/packages/fs/tool-fs/tests/fs-tools.e2e.ts +++ b/packages/fs/tool-fs/tests/fs-tools.e2e.ts @@ -43,6 +43,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('fs tools with-key smoke', () => expect(content).not.toContain('draft') // The log records real read/write/edit tool calls (not bash). + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const calls = agent.session.snapshotEvents().filter(e => e.type === 'tool/call').map(e => e.data.name) expect(calls).toContain('write') expect(calls).toContain('read') diff --git a/packages/fs/tool-present/tests/present.spec.ts b/packages/fs/tool-present/tests/present.spec.ts index 95606e09dc..4c805c2395 100644 --- a/packages/fs/tool-present/tests/present.spec.ts +++ b/packages/fs/tool-present/tests/present.spec.ts @@ -84,6 +84,7 @@ describe('present', () => { if (result.isError) throw new Error('present failed') const files = (result.value as unknown as { files: PresentedFile[] }).files expect(files).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().find(event => event.type === 'deliverables/presented')?.data.files).toEqual(files) expect(files).toEqual([{ path: '报告.docx', description: 'Report' }]) expect(read).not.toHaveBeenCalled() @@ -109,6 +110,7 @@ describe('present', () => { execute: async () => ({ turn: 1, files: [] }), })) expect((await execute([])).isError).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().filter(event => event.type === 'deliverables/presented')).toEqual([]) }) @@ -117,6 +119,7 @@ describe('present', () => { await owner.ctx.plugin(Present, { maxFiles: 2 }) await writeFile(join(root, 'a'), 'a') expect((await execute([{ path: 'a' }])).isError).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const deliveries = owner.session.snapshotEvents().filter(event => event.type === 'deliverables/presented') expect(deliveries).toHaveLength(1) expect(deliveries[0]?.data.files[0]?.path).toBe('a') @@ -130,6 +133,7 @@ describe('present', () => { return { kind: 'block', feedback: [{ type: 'text', text: 'blocked' }] } }) expect((await execute([{ path: 'a' }])).isError).toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().some(event => event.type === 'deliverables/presented')).toBe(false) }) @@ -141,6 +145,7 @@ describe('present', () => { const result = await execute(files) expect(result.isError, JSON.stringify(files)).toBe(true) } + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().some(event => event.type === 'deliverables/presented')).toBe(false) }) diff --git a/packages/goal/command-goal/tests/command-goal.spec.ts b/packages/goal/command-goal/tests/command-goal.spec.ts index 1c1761cf74..94f42cdfe8 100644 --- a/packages/goal/command-goal/tests/command-goal.spec.ts +++ b/packages/goal/command-goal/tests/command-goal.spec.ts @@ -59,15 +59,19 @@ async function harness(): Promise { /** The log with executor-owned command lifecycle bookkeeping stripped (goal assertions target domain events). */ function domainEvents(session: Session): readonly SessionEvent[] { const lifecycle = new Set() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { if (event.type !== 'command/run' && event.type !== 'command/done') continue lifecycle.add(event.seq) // The zero-step wrap around a lifecycle event is bookkeeping too. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents()[event.seq - 1] + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const after = session.snapshotEvents()[event.seq + 1] if (before?.type === 'turn/start') lifecycle.add(before.seq) if (after?.type === 'turn/end') lifecycle.add(after.seq) } + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents().filter(event => !lifecycle.has(event.seq)) } diff --git a/packages/goal/goal-round-driver/src/invariant.ts b/packages/goal/goal-round-driver/src/invariant.ts index 22ca5c39a1..e716422f27 100644 --- a/packages/goal/goal-round-driver/src/invariant.ts +++ b/packages/goal/goal-round-driver/src/invariant.ts @@ -61,6 +61,7 @@ function validateEvent( const install: InvariantInstaller = Object.assign((ctx: Context, fail: InvariantFailure) => { for (const session of ctx.sessions.list()) { const prior: SessionEvent[] = [] + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { validateEvent(prior, event, fail) prior.push(event) @@ -70,6 +71,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant ctx.on('internal/dispatch', (_mode, eventName, args) => { if (eventName !== 'session/event') return const [session, event] = args as [Session, SessionEvent] + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. validateEvent(session.snapshotEvents(), event, fail) }, { global: true }) }, { inject: ['sessions'] }) diff --git a/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts b/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts index 2ec805d71b..4732334ddc 100644 --- a/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts +++ b/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts @@ -197,6 +197,7 @@ describe('same-session goal driving', () => { }) expect(test.adapter.requests).toHaveLength(2) const rounds: number[] = [] + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of test.agent.session.snapshotEvents()) { // Round zero is a durable goal state change; positive rounds are the // admitted continuation prompts this test counts. @@ -207,6 +208,7 @@ describe('same-session goal driving', () => { expect(rounds).toEqual([1, 2]) expect(requestText(test.adapter.requests[0]!)).toContain('Round: 1/2') expect(requestText(test.adapter.requests[1]!)).toContain('Round: 2/2') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().flatMap(event => event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series']) }) @@ -262,6 +264,7 @@ describe('same-session goal driving', () => { message: 'Goal round was rejected before entering its step.', }) expect(test.adapter.requests).toHaveLength(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true) }) @@ -299,6 +302,7 @@ describe('same-session goal driving', () => { expect(test.adapter.requests).toHaveLength(0) // No admitted continuation round reached the model; goal state changes are // represented by their own durable event. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'user/message' && event.data.source.kind === 'goal' && event.data.source.round > 0)).toBe(false) }) @@ -377,6 +381,7 @@ describe('same-session goal driving', () => { expect(goal).toMatchObject({ phase: 'paused', roundsStarted: 1 }) expect(test.adapter.requests).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEndKinds = test.agent.session.snapshotEvents().flatMap(event => event.type === 'turn/end' ? [event.data.reason.kind] : []) expect(turnEndKinds).toContain('completed') @@ -394,6 +399,7 @@ describe('same-session goal driving', () => { expect(requestText(test.adapter.requests[0]!)).toContain('human goes first') expect(requestText(test.adapter.requests[0]!)).not.toContain('') expect(requestText(test.adapter.requests[1]!)).toContain('') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().flatMap(event => event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series']) }) @@ -431,6 +437,7 @@ describe('same-session goal driving', () => { const goal = await waitForGoal(test.ctx, test.agent, current => current?.phase === 'blocked') expect(goal).toMatchObject({ revision: 3, objective: 'new objective', roundsStarted: 1 }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const admitted = test.agent.session.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'goal' && event.data.source.round > 0) expect(admitted?.type === 'user/message' && admitted.data.source.kind === 'goal' @@ -817,6 +824,7 @@ describe('same-session goal driving', () => { await test.agent.whenIdle() expect(test.adapter.requests).toHaveLength(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true) }) @@ -991,6 +999,7 @@ describe('same-session goal driving', () => { }) handle.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'one ordinary turn' }], source: { kind: 'user' } })) await handle.agent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const closed = handle.agent.session.snapshotEvents().findLast(event => event.type === 'turn/end') if (closed?.type !== 'turn/end') throw new Error('expected a closed turn') await handle.dispose() @@ -1096,6 +1105,7 @@ describe('same-session goal driving', () => { expect(test.ctx.goals.get(test.agent)).toMatchObject({ phase: 'active', roundsStarted: 0 }) expect(test.adapter.requests).toHaveLength(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true) }) diff --git a/packages/goal/goal/src/invariant.ts b/packages/goal/goal/src/invariant.ts index 3d8e6a46f0..8c0004f42a 100644 --- a/packages/goal/goal/src/invariant.ts +++ b/packages/goal/goal/src/invariant.ts @@ -43,6 +43,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant const seed = (session: Session): GoalFoldState => { const state = emptyGoalFoldState() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) applyChecked(state, event, fail) states.set(session, state) return state diff --git a/packages/goal/goal/tests/goal.spec.ts b/packages/goal/goal/tests/goal.spec.ts index ac32cd3765..7281859055 100644 --- a/packages/goal/goal/tests/goal.spec.ts +++ b/packages/goal/goal/tests/goal.spec.ts @@ -27,6 +27,7 @@ const sessionStubs = new WeakMap() /** Number the next balanced test-fixture turn. */ function nextTurn(session: Session): number { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents().reduce((max, event) => event.type === 'turn/start' ? Math.max(max, event.data.turn) : max, 0) + 1 } @@ -131,7 +132,9 @@ describe('GoalService creation and replay', () => { }) expect(goal.id).toMatch(/^goal-/) expect(seen).toEqual(['create']) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual(['goal/change']) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const context = session.snapshotEvents()[0] expect(context?.type).toBe('goal/change') if (context?.type !== 'goal/change') throw new Error('expected durable goal change') @@ -140,6 +143,7 @@ describe('GoalService creation and replay', () => { expect(change).toMatchObject({ operation: 'create', goal: { id: goal.id } }) expect(agent.inbox.nextStep).toEqual([]) expect(session.deriveMessages()).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { id: goal.id }, roundsStarted: 0 }) vi.useRealTimers() }) @@ -192,6 +196,7 @@ describe('GoalService creation and replay', () => { await ctx.plugin(AgentRegistry) await ctx.plugin(SessionProjectionRegistry) await ctx.plugin(GoalService) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = stubAgent('seeded-goal', first.session.snapshotEvents()) ctx.agents.register(resumed.agent) expect(ctx.goals.get(resumed.agent)).toMatchObject({ @@ -240,12 +245,14 @@ describe('GoalService creation and replay', () => { expect(activations.map(entry => entry.activation)).toEqual(['armed', 'disarmed', 'armed']) expect(activations.map(entry => entry.id)).toEqual([goal.id, goal.id, goal.id]) expect(activations.map(entry => entry.revision)).toEqual([1, 1, 2]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).not.toThrow() }) it('lets a lifecycle owner disarm without writing a durable revision', async () => { const { ctx, agent, session } = await harness() const goal = ctx.goals.create(agent, { objective: 'survive driver reload' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents().length expect(ctx.goals.disarm(agent)).toMatchObject({ id: goal.id, @@ -253,6 +260,7 @@ describe('GoalService creation and replay', () => { phase: 'active', activation: 'disarmed', }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(before) expect(ctx.goals.resume(agent, goal)).toMatchObject({ revision: 2, activation: 'armed' }) }) @@ -416,6 +424,7 @@ describe('GoalService mutations', () => { const tombstone = ctx.goals.clear(agent, goal) expect(tombstone).toEqual({ id: goal.id, revision: 2 }) expect(ctx.goals.get(agent)).toBeUndefined() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0, lastRef: tombstone }) expect(() => ctx.goals.clear(agent, goal)).toThrow(expect.objectContaining({ code: 'GOAL_NOT_FOUND' })) const next = ctx.goals.create(agent, { objective: 'fresh' }) @@ -432,11 +441,13 @@ describe('GoalService mutations', () => { expect(goal.updatedAt).toBe(100) vi.setSystemTime(80) ctx.goals.clear(agent, goal) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const clear = session.snapshotEvents() .filter(event => event.type === 'goal/change') .map(event => event.type === 'goal/change' ? decodeGoalChange(event.data) : undefined) .at(-1) expect(clear).toMatchObject({ operation: 'clear', clearedAt: 100 }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).not.toThrow() vi.useRealTimers() }) @@ -458,10 +469,12 @@ describe('GoalService mutations', () => { goal = ctx.goals.edit(agent, goal, { objective: 'deferred edit' }) goal = ctx.goals.pause(agent, goal) expect(goal).toMatchObject({ revision: 3, phase: 'paused', activation: 'disarmed' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual([ 'goal/change', 'goal/change', 'goal/change', ]) expect(ctx.goals.get(agent)).toMatchObject({ revision: 3, phase: 'paused' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { revision: 3, phase: 'paused' } }) }) @@ -482,6 +495,7 @@ describe('GoalService mutations', () => { expect(observed).toEqual(created) expect(ctx.goals.get(stub.agent)).toEqual(created) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(stub.session.snapshotEvents())).toMatchObject({ goal: { id: created.id, revision: 1 } }) }) @@ -499,6 +513,7 @@ describe('GoalService mutations', () => { revision: 1, }) expect(stub.agent.inbox.nextStep).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(stub.session.snapshotEvents().map(event => event.type)).toEqual(['goal/change']) }) @@ -584,6 +599,7 @@ describe('goal replay validation', () => { function oneChange(change: GoalChangeMeta) { const session = Session.create(SessionId(`validation-${Math.random()}`)) appendChange(session, change) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents() } @@ -615,6 +631,7 @@ describe('goal replay validation', () => { const change = snapshotChange() const session = Session.create(SessionId('inbox-independent-change')) appendChange(session, change) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { id: change.goal.id, revision: 1 } }) const message = createUserMessage({ content: [{ type: 'text', text: 'unrelated pending context' }], @@ -623,6 +640,7 @@ describe('goal replay validation', () => { const inbox = stubAgentForSession(session).agent.inbox inbox.append('next-step', message) expect(inbox.remove(message.id)).toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { id: change.goal.id, revision: 1 } }) }) @@ -630,6 +648,7 @@ describe('goal replay validation', () => { const session = Session.create(SessionId(`validation-pair-${Math.random()}`)) appendChange(session, first) appendChange(session, second) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return foldGoal(session.snapshotEvents()) } @@ -641,6 +660,7 @@ describe('goal replay validation', () => { content: [{ type: 'text', text: 'other' }], source: { kind: 'plugin', plugin: 'test' }, })) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0 }) const source = { kind: 'plugin', plugin: 'ordinary-user-message' } as const const turn = nextTurn(session) @@ -649,6 +669,7 @@ describe('goal replay validation', () => { content: [{ type: 'text', text: 'ordinary' }], source, }), { surfaceOp: 'append' }) session.append('turn/end', { turn, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0 }) }) @@ -656,6 +677,7 @@ describe('goal replay validation', () => { const change = snapshotChange() const session = Session.create(SessionId('other-goal-round'), oneChange(change)) appendRound(session, { id: GoalId('goal-other'), revision: 1 }, 1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).toThrow('not the next admitted round') }) @@ -732,6 +754,7 @@ describe('goal replay validation', () => { appendRound(session, base.goal, 2) appendChange(session, { ...paused, roundsStarted: 2 }) appendChange(session, exhausted) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).toThrow('exhausted round budget') }) @@ -756,6 +779,7 @@ describe('goal replay validation', () => { appendChange(completedSession, base) appendChange(completedSession, complete) appendChange(completedSession, sameCurrentId) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(completedSession.snapshotEvents())).toThrow('fresh active revision-one') const second = snapshotChange({ @@ -770,6 +794,7 @@ describe('goal replay validation', () => { appendChange(nonAdjacentReuse, second) appendChange(nonAdjacentReuse, secondComplete) appendChange(nonAdjacentReuse, { ...sameCurrentId, createdAt: 30, updatedAt: 30 }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(nonAdjacentReuse.snapshotEvents())).toThrow('fresh active revision-one') const clear: GoalChangeMeta = { @@ -779,6 +804,7 @@ describe('goal replay validation', () => { appendChange(clearedSession, base) appendChange(clearedSession, clear) appendChange(clearedSession, sameCurrentId) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(clearedSession.snapshotEvents())).toThrow('fresh active revision-one') }) @@ -791,6 +817,7 @@ describe('goal replay validation', () => { content: [{ type: 'text', text: 'missing' }], source, }), { surfaceOp: 'append' }) session.append('turn/end', { turn, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).toThrow('goal message source is invalid') }) @@ -838,6 +865,7 @@ describe('goal replay validation', () => { clearedAt: 20, } appendChange(session, clear) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0, lastRef: { id: change.goal.id, revision: 2 }, diff --git a/packages/goal/goal/tests/projection.spec.ts b/packages/goal/goal/tests/projection.spec.ts index 45cd1ddb37..77c2e23818 100644 --- a/packages/goal/goal/tests/projection.spec.ts +++ b/packages/goal/goal/tests/projection.spec.ts @@ -137,6 +137,7 @@ describe('goal projection unit', () => { }) expect(bench.tailValues().goal).toBeNull() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(bench.session.snapshotEvents()).goal).toBeUndefined() }) diff --git a/packages/goal/tool-goal/src/authority.ts b/packages/goal/tool-goal/src/authority.ts index 34d3108e5d..eadc0bd8f9 100644 --- a/packages/goal/tool-goal/src/authority.ts +++ b/packages/goal/tool-goal/src/authority.ts @@ -30,6 +30,7 @@ function openTurnEvents( ctx: Context, agent: Agent, ): Pick { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = agent.session.snapshotEvents() const boundary = ctx.sessionProjections.stateOf(agent.session, 'turnBoundary') if (boundary === undefined || boundary.openTurnStartSeq === null) { diff --git a/packages/goal/tool-goal/tests/tool-goal.spec.ts b/packages/goal/tool-goal/tests/tool-goal.spec.ts index b4b4d54808..00b2feabe2 100644 --- a/packages/goal/tool-goal/tests/tool-goal.spec.ts +++ b/packages/goal/tool-goal/tests/tool-goal.spec.ts @@ -68,6 +68,7 @@ function stubAgent(rawId: string, supplied?: Session, suppliedCtx?: Context): St /** Open one message-triggered turn with its accepted model-visible input. */ function openTurn(stub: StubAgent, source: MessageSource, text = 'prompt'): number { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turn = stub.session.snapshotEvents() .filter(event => event.type === 'turn/start') .reduce((max, event) => Math.max(max, event.data.turn), 0) + 1 @@ -282,6 +283,7 @@ describe('goal tool execution authority', () => { const created = ctx.goals.create(root.agent, { objective: 'resume the fork' }) closeTurn(root, originalTurn) const forkId = SessionId('goal-tool-resumed-fork') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const forkSession = Session.create(forkId, root.session.snapshotEvents(), { version: SESSION_FORMAT_VERSION, id: forkId, diff --git a/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts b/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts index 4c505d6cf0..e83b99d1ec 100644 --- a/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts +++ b/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts @@ -37,6 +37,7 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { /** Every injected-context user message in the agent's log, flattened to joined text + source for terse assertions. */ function reminders(agent: Agent): { text: string; source: unknown }[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() .filter((e): e is SessionEvent<'user/message'> => e.type === 'user/message' && e.data.source.kind !== 'user') .map(e => ({ @@ -337,6 +338,7 @@ describe('fold onto the downstream decision', () => { expect(found[1]!.source).toEqual(guardSource('probe', 2)) expect(found[2]).toEqual({ text: 'downstream-ctx', source: { kind: 'plugin', plugin: 'test' } }) // The block's feedback reached the tool result unchanged. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = agent.session.snapshotEvents().filter((e): e is SessionEvent<'tool/result'> => e.type === 'tool/result') expect(results.every(r => r.data.message.content[0].isError)).toBe(true) expect(results[1]!.data.message.content[0].content).toEqual([{ type: 'text', text: 'nope' }]) @@ -361,6 +363,7 @@ describe('fold onto the downstream decision', () => { const found = reminders(agent) expect(found).toHaveLength(1) expect(found[0]!.text).toContain('repeating the exact same tool call') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = agent.session.snapshotEvents().filter((e): e is SessionEvent<'tool/result'> => e.type === 'tool/result') expect(results[1]!.data.message.content[0].content).toEqual([{ type: 'text', text: 'replaced' }]) }) diff --git a/packages/hooks/hook-protocol/src/invariant.ts b/packages/hooks/hook-protocol/src/invariant.ts index 660241f8fd..73a817f7de 100644 --- a/packages/hooks/hook-protocol/src/invariant.ts +++ b/packages/hooks/hook-protocol/src/invariant.ts @@ -74,6 +74,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant const seed = (session: Session): HookTrace => { const trace: HookTrace = { openTurn: null, pending: new Map() } traces.set(session, trace) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { if (event.type === 'turn/start') trace.openTurn = event.data.turn else if (event.type === 'turn/end') trace.openTurn = null diff --git a/packages/hooks/hook-protocol/tests/events.spec.ts b/packages/hooks/hook-protocol/tests/events.spec.ts index 45aeca1050..5da0f91911 100644 --- a/packages/hooks/hook-protocol/tests/events.spec.ts +++ b/packages/hooks/hook-protocol/tests/events.spec.ts @@ -12,6 +12,7 @@ describe('hook/* session events', () => { const session = Session.create(SessionId('s')) appendHookInvoked(session, { turn: 1, point: 'PreToolUse', dialect: 'claude-code', handlerId: 'h1', matcher: 'Bash' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/invoked') expect(ev?.type).toBe('hook/invoked') if (ev?.type === 'hook/invoked') { @@ -25,6 +26,7 @@ describe('hook/* session events', () => { const session = Session.create(SessionId('s')) appendHookInvoked(session, { turn: 2, point: 'Stop', dialect: 'codex', handlerId: 'h2' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/invoked') if (ev?.type === 'hook/invoked') { expect('matcher' in ev.data).toBe(false) @@ -37,6 +39,7 @@ describe('hook/* session events', () => { turn: 1, point: 'PreToolUse', handlerId: 'h1', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: 2, stderr: 'blocked', decision: 'deny' }), }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const full = session.snapshotEvents().find(e => e.type === 'hook/result') if (full?.type === 'hook/result') { expect(full.data).toEqual({ turn: 1, point: 'PreToolUse', handlerId: 'h1', decision: 'deny', exitCode: 2, stderrSummary: 'blocked', durationMs: 5 }) @@ -48,6 +51,7 @@ describe('hook/* session events', () => { turn: 1, point: 'Stop', handlerId: 'h3', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: undefined, decision: 'allow' }), }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const sparse = session2.snapshotEvents().find(e => e.type === 'hook/result') if (sparse?.type === 'hook/result') { expect('exitCode' in sparse.data).toBe(false) @@ -63,6 +67,7 @@ describe('hook/* session events', () => { // An explicit decision wins over the continue:false fallback. appendHookResult(session, { turn: 1, point: 'Stop', handlerId: 'both', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ continue: false, decision: 'block' }) }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decisions = session.snapshotEvents() .filter(e => e.type === 'hook/result') .map(e => e.type === 'hook/result' ? [e.data.handlerId, e.data.decision] : []) @@ -75,6 +80,7 @@ describe('hook/* session events', () => { turn: 1, point: 'PreToolUse', handlerId: 'long', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: 2, stderr: ` ${'x'.repeat(600)} ` }), }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/result') if (ev?.type === 'hook/result') { expect(ev.data.stderrSummary).toBe('x'.repeat(500) + '…') @@ -87,6 +93,7 @@ describe('hook/* session events', () => { turn: 1, point: 'PreToolUse', handlerId: 'edge', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: 2, stderr: 'y'.repeat(500) }), }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/result') if (ev?.type === 'hook/result') { expect(ev.data.stderrSummary).toBe('y'.repeat(500)) @@ -98,7 +105,9 @@ describe('hook/* session events', () => { appendHookInvoked(session, { turn: 1, point: 'PreToolUse', dialect: 'claude-code', handlerId: 'pair-1' }) appendHookResult(session, { turn: 1, point: 'PreToolUse', handlerId: 'pair-1', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ decision: 'allow' }) }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const invoked = session.snapshotEvents().find(e => e.type === 'hook/invoked') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const result = session.snapshotEvents().find(e => e.type === 'hook/result') expect(invoked?.type === 'hook/invoked' && invoked.data.handlerId).toBe('pair-1') expect(result?.type === 'hook/result' && result.data.handlerId).toBe('pair-1') diff --git a/packages/hooks/hooks-claude-code/tests/bridge.spec.ts b/packages/hooks/hooks-claude-code/tests/bridge.spec.ts index 68f6c8319d..ec6a9c2453 100644 --- a/packages/hooks/hooks-claude-code/tests/bridge.spec.ts +++ b/packages/hooks/hooks-claude-code/tests/bridge.spec.ts @@ -71,6 +71,7 @@ function waitForIdle(_ctx: Context, agent: Agent): Promise { } function events(agent: Agent): readonly SessionEvent[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() } diff --git a/packages/hooks/hooks-claude-code/tests/coverage-cases.ts b/packages/hooks/hooks-claude-code/tests/coverage-cases.ts index 8627a970cd..1edfcb2500 100644 --- a/packages/hooks/hooks-claude-code/tests/coverage-cases.ts +++ b/packages/hooks/hooks-claude-code/tests/coverage-cases.ts @@ -52,6 +52,7 @@ async function harness(configPath: string, adapter: MockAdapter, opts: HarnessOp function waitForIdle(_ctx: Context, agent: Agent): Promise { return agent.whenIdle() } +// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. function events(agent: Agent): readonly SessionEvent[] { return agent.session.snapshotEvents() } /** Poll until `predicate` holds or the deadline passes — robust to detached * emit-listener hooks firing on a `.then` (a fixed sleep flakes under load). */ diff --git a/packages/hooks/hooks-codex/tests/bridge.spec.ts b/packages/hooks/hooks-codex/tests/bridge.spec.ts index 1ef87de7c1..2ffb062a0c 100644 --- a/packages/hooks/hooks-codex/tests/bridge.spec.ts +++ b/packages/hooks/hooks-codex/tests/bridge.spec.ts @@ -54,6 +54,7 @@ async function harness(dir: string, adapter: MockAdapter, beforeHooks?: (ctx: Co function waitForIdle(_ctx: Context, agent: Agent): Promise { return agent.whenIdle() } +// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. function events(agent: Agent): readonly SessionEvent[] { return agent.session.snapshotEvents() } /** Poll `predicate` until true or the deadline passes (detached hook effects can't be awaited directly). */ diff --git a/packages/hooks/hooks-codex/tests/coverage-cases.ts b/packages/hooks/hooks-codex/tests/coverage-cases.ts index 12a3c370ab..91956f33a5 100644 --- a/packages/hooks/hooks-codex/tests/coverage-cases.ts +++ b/packages/hooks/hooks-codex/tests/coverage-cases.ts @@ -42,6 +42,7 @@ async function harness(configPath: string, adapter: MockAdapter, opts: HarnessOp function waitForIdle(_ctx: Context, agent: Agent): Promise { return agent.whenIdle() } +// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. function events(agent: Agent): readonly SessionEvent[] { return agent.session.snapshotEvents() } /** Poll until `predicate` holds or the deadline passes — robust to detached * emit-listener hooks firing on a `.then` (a fixed sleep flakes under load). */ diff --git a/packages/interaction/commands/src/invariant.ts b/packages/interaction/commands/src/invariant.ts index 7bce241224..5eb8194b1a 100644 --- a/packages/interaction/commands/src/invariant.ts +++ b/packages/interaction/commands/src/invariant.ts @@ -35,6 +35,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant fail(`command/done ${JSON.stringify(event.data.commandId)} pairs no prior command/run in this log`) } const source = event.data.sourceEventSeq + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const sourceEvent = source === undefined ? undefined : session.eventAt(source) if (source !== undefined && (event.data.kind !== 'success' @@ -46,6 +47,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant } } for (const session of ctx.sessions.list()) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) validateEvent(session, event) } ctx.on('internal/dispatch', (_mode, eventName, args) => { diff --git a/packages/interaction/commands/tests/commands.spec.ts b/packages/interaction/commands/tests/commands.spec.ts index 92a12d921d..aef91060c8 100644 --- a/packages/interaction/commands/tests/commands.spec.ts +++ b/packages/interaction/commands/tests/commands.spec.ts @@ -33,6 +33,7 @@ async function mintAgentScope(ctx: Context, name: string): Promise<{ scope: Scop /** The lifecycle slice of one agent's log (boundary markers stripped). */ function lifecycleOf(agent: Agent): Array<{ type: string; data: unknown }> { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() .filter(event => event.type === 'command/run' || event.type === 'command/done') .map(event => ({ type: event.type, data: event.data })) @@ -316,6 +317,7 @@ describe('CommandRuntime', () => { // The execution's pairing id is the logged one (RPC-level correlation). expect(execution?.commandId).toBe(ids[0]) // Direct log-only appends: no turn is opened for the pair on an idle log. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().map(event => event.type)).toEqual([ 'command/run', 'command/done', ]) @@ -354,6 +356,7 @@ describe('CommandRuntime', () => { await ctx.commands.execute(agent, '/private keep this once', [], new AbortController().signal) expect(seen).toHaveBeenCalledWith(expect.objectContaining({ rawInput: ' keep this once' })) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const run = agent.session.snapshotEvents().find(event => event.type === 'command/run') expect(run?.type).toBe('command/run') expect(run?.type === 'command/run' && Object.hasOwn(run.data, 'args')).toBe(false) @@ -428,6 +431,7 @@ describe('CommandRuntime', () => { const signal = new AbortController().signal await ctx.commands.execute(agent, 'not a command', [], signal) await ctx.commands.execute(agent, '/missing', [], signal) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents()).toEqual([]) }) @@ -437,6 +441,7 @@ describe('CommandRuntime', () => { ctx.commands.register(command('mid')) agent.session.append('turn/start', { turn: 1 }) await ctx.commands.execute(agent, '/mid', [], new AbortController().signal) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().map(event => event.type)).toEqual([ 'turn/start', 'command/run', 'command/done', ]) diff --git a/packages/interaction/permission-presets/src/invariant.ts b/packages/interaction/permission-presets/src/invariant.ts index dee9db2451..700b42ba6c 100644 --- a/packages/interaction/permission-presets/src/invariant.ts +++ b/packages/interaction/permission-presets/src/invariant.ts @@ -21,6 +21,7 @@ function validateEvent(ctx: Context, event: SessionEvent, fail: InvariantFailure /** Install validation that loaded and newly appended preset events remain resolvable. */ const install: InvariantInstaller = Object.assign((ctx: Context, fail: InvariantFailure) => { for (const session of ctx.sessions.list()) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) validateEvent(ctx, event, fail) } ctx.on('internal/dispatch', (_mode, eventName, args) => { diff --git a/packages/interaction/permission-presets/tests/permission-presets.spec.ts b/packages/interaction/permission-presets/tests/permission-presets.spec.ts index 3da5749e2b..552373474f 100644 --- a/packages/interaction/permission-presets/tests/permission-presets.spec.ts +++ b/packages/interaction/permission-presets/tests/permission-presets.spec.ts @@ -151,6 +151,7 @@ describe('PermissionPresetService', () => { const ctx = await mounted() const session = freshSession('sess-set') ctx.permissionPresets.set(session, 'danger-full-access') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(e => [e.type, e.data])).toEqual([ ['permission/preset', { preset: 'danger-full-access' }], ['sandbox/mode', { mode: 'danger-full-access' }], @@ -162,6 +163,7 @@ describe('PermissionPresetService', () => { const ctx = await mounted() const session = freshSession('sess-noop') ctx.permissionPresets.set(session, 'workspace-write') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(0) }) @@ -173,6 +175,7 @@ describe('PermissionPresetService', () => { // the changed knob. session.append('sandbox/mode', { mode: 'read-only' }) ctx.permissionPresets.set(session, 'danger-full-access') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const tail = session.snapshotEvents().slice(4) expect(tail.map(e => [e.type, e.data])).toEqual([ ['permission/preset', { preset: 'danger-full-access' }], @@ -208,6 +211,7 @@ describe('PermissionPresetService', () => { const ctx = await mounted({ approvalDefault: undefined }) const session = freshSession('sess-standin') ctx.permissionPresets.set(session, 'workspace-write') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(0) expect(ctx.permissionPresets.current(session)).toBe('workspace-write') }) @@ -217,6 +221,7 @@ describe('new-session default', () => { it('pins the current setting into each new session without changing earlier sessions', async () => { const ctx = await mountedStore() const first = ctx.sessions.create(SessionId('first')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(first.snapshotEvents().map(event => [event.type, event.data])).toEqual([ ['permission/preset', { preset: 'workspace-write' }], ['sandbox/mode', { mode: 'workspace-write' }], @@ -230,6 +235,7 @@ describe('new-session default', () => { const second = ctx.sessions.create(SessionId('second')) expect(ctx.permissionPresets.current(first)).toBe('workspace-write') expect(ctx.permissionPresets.current(second)).toBe('danger-full-access') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(second.snapshotEvents().map(event => event.type)).toEqual([ 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -243,8 +249,10 @@ describe('new-session default', () => { const legacy = freshSession('legacy-source') legacy.append('turn/start', { turn: 1 }) legacy.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = ctx.sessions.create(SessionId('legacy-resumed'), { seed: legacy.snapshotEvents() }) expect(ctx.permissionPresets.current(resumed)).toBe('workspace-write') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().slice(-3).map(event => event.type)).toEqual([ 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -257,6 +265,7 @@ describe('new-session default', () => { }) const resumed = ctx.sessions.create(SessionId('empty-resumed'), { seed: [] }) expect(ctx.permissionPresets.current(resumed)).toBe('workspace-write') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().map(event => event.type)).toEqual([ 'session/end-seed', 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -274,9 +283,11 @@ describe('new-session default', () => { }) ctx.provide('approval', { config: { policy: 'ask' } }) const existing = ctx.sessions.create(SessionId('existing-before-permission')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(existing.snapshotEvents()).toEqual([]) await ctx.plugin(PermissionPresetService, {}) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(existing.snapshotEvents().map(event => event.type)).toEqual([ 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -302,6 +313,7 @@ describe('new-session default', () => { // The remount sweep must read the folded knob events instead of treating // the session as fresh; no default preset events may overwrite the // overrides (read-only + never matches no preset table entry). + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(existing.snapshotEvents().map(event => event.type)).toEqual([ 'sandbox/mode', 'approval/policy', ]) @@ -313,7 +325,9 @@ describe('new-session default', () => { const partial = freshSession('partial-source') partial.append('sandbox/mode', { mode: 'workspace-write' }) partial.append('approval/policy', { policy: 'ask' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = ctx.sessions.create(SessionId('partial-resumed'), { seed: partial.snapshotEvents() }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().at(-1)).toMatchObject({ type: 'permission/preset', data: { preset: 'workspace-write' }, @@ -322,8 +336,10 @@ describe('new-session default', () => { const custom = freshSession('custom-source') custom.append('sandbox/mode', { mode: 'read-only' }) custom.append('approval/policy', { policy: 'never' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const unmatched = ctx.sessions.create(SessionId('custom-resumed'), { seed: custom.snapshotEvents() }) expect(ctx.permissionPresets.current(unmatched)).toBe(CUSTOM_PRESET) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(unmatched.snapshotEvents().at(-1)?.type).toBe('session/end-seed') }) @@ -331,7 +347,9 @@ describe('new-session default', () => { const ctx = await mountedStore({ approvalDefault: undefined }) const partial = freshSession('approval-fallback-source') partial.append('sandbox/mode', { mode: 'workspace-write' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = ctx.sessions.create(SessionId('approval-fallback-resumed'), { seed: partial.snapshotEvents() }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().at(-1)).toMatchObject({ type: 'approval/policy', data: { policy: 'ask' }, diff --git a/packages/interaction/permission-presets/tests/projection.spec.ts b/packages/interaction/permission-presets/tests/projection.spec.ts index 53022e9089..9a7ef5d0d3 100644 --- a/packages/interaction/permission-presets/tests/projection.spec.ts +++ b/packages/interaction/permission-presets/tests/projection.spec.ts @@ -99,6 +99,7 @@ describe('/permission command', () => { text: 'The approval policy changed from "ask" to "never" (changed by the user).', }], }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const run = session.snapshotEvents().find(event => event.type === 'command/run') expect(run?.data).toMatchObject({ name: 'permission', args: ' danger-full-access' }) }) @@ -111,12 +112,14 @@ describe('/permission command', () => { kind: 'success', text: 'current preset workspace-write (available: workspace-write, danger-full-access)', }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'permission/preset')).toHaveLength(1) }) it('rejects an unknown preset without touching the log', async () => { const { ctx, session } = await harness() const { agent } = await agentFor(ctx, session) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents().filter(event => event.type !== 'command/run' && event.type !== 'command/done') const execution = await ctx.commands.execute(agent, '/permission yolo', [], new AbortController().signal) @@ -127,6 +130,7 @@ describe('/permission command', () => { kind: 'error', text: 'unknown preset "yolo" (available: workspace-write, danger-full-access)', }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type !== 'command/run' && event.type !== 'command/done')).toEqual(before) }) diff --git a/packages/interaction/user-approval/src/index.ts b/packages/interaction/user-approval/src/index.ts index 8e1a8d05d4..8a5f506bf1 100644 --- a/packages/interaction/user-approval/src/index.ts +++ b/packages/interaction/user-approval/src/index.ts @@ -76,6 +76,7 @@ const ASK_SENTENCE = 'Approval policy: ask. Operations that require approval may */ function hasOpenTurn(session: Session): boolean { for (let seq = session.seq - 1; seq >= 0; seq -= 1) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const type = session.eventAt(SessionSeq(seq))?.type if (type === 'turn/start') return true if (type === 'turn/end') return false @@ -243,6 +244,7 @@ export class ApprovalService extends Service { */ overrideOf(session: Session): ApprovalPolicy | undefined { for (let seq = session.seq - 1; seq >= 0; seq -= 1) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.eventAt(SessionSeq(seq)) if (event?.type === 'approval/policy') return event.data.policy } diff --git a/packages/interaction/user-approval/src/invariant.ts b/packages/interaction/user-approval/src/invariant.ts index 8ff7a9953e..d356ca3698 100644 --- a/packages/interaction/user-approval/src/invariant.ts +++ b/packages/interaction/user-approval/src/invariant.ts @@ -64,6 +64,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant const seed = (session: Session): ApprovalTrace => { const trace: ApprovalTrace = { openTurn: null, pending: new Set() } traces.set(session, trace) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { if (event.type === 'turn/start') trace.openTurn = event.data.turn else if (event.type === 'turn/end') trace.openTurn = null diff --git a/packages/interaction/user-approval/tests/approval.spec.ts b/packages/interaction/user-approval/tests/approval.spec.ts index 70e1abcf4e..c0fce76425 100644 --- a/packages/interaction/user-approval/tests/approval.spec.ts +++ b/packages/interaction/user-approval/tests/approval.spec.ts @@ -132,8 +132,11 @@ describe('ApprovalService.request', () => { await expect(ctx.approval.request(requestOf(agent))).resolves.toBe('allowed-once') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const audit = session.snapshotEvents().filter(event => event.type.startsWith('approval/')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const asked = session.snapshotEvents().find((event): event is SessionEvent<'approval/asked'> => event.type === 'approval/asked') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decided = session.snapshotEvents().find((event): event is SessionEvent<'approval/decided'> => event.type === 'approval/decided') expect(audit.map(event => event.type)).toEqual(['approval/asked', 'approval/decided']) expect(decided?.data.id).toBe(asked?.data.id) @@ -155,8 +158,11 @@ describe('ApprovalService.request', () => { await expect(ctx.approval.request(requestOf(agent))).resolves.toBe('rejected') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const audit = session.snapshotEvents().filter(event => event.type.startsWith('approval/')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const asked = session.snapshotEvents().find((event): event is SessionEvent<'approval/asked'> => event.type === 'approval/asked') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decided = session.snapshotEvents().find((event): event is SessionEvent<'approval/decided'> => event.type === 'approval/decided') expect(audit.map(event => event.type)).toEqual(['approval/asked', 'approval/decided']) expect(decided?.data).toMatchObject({ id: asked?.data.id, outcome: 'rejected' }) @@ -376,6 +382,7 @@ describe('approval policy (the approval/policy fold)', () => { setApprovalPolicy(session, 'never') setApprovalPolicy(session, 'ask') expect(service.overrideOf(session)).toBe('ask') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().at(-1)).toMatchObject({ type: 'approval/policy', data: { policy: 'ask' } }) }) @@ -415,7 +422,9 @@ describe('approval policy (the approval/policy fold)', () => { await expect(ctx.approval.request({ agent, toolName: 'bash' })).resolves.toBe('rejected') expect(consulted).not.toHaveBeenCalled() // The audit pair still lands on the session log. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(e => e.type === 'approval/asked')).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(e => e.type === 'approval/decided')).toHaveLength(1) }) diff --git a/packages/llm/llm-retry/src/invariant.ts b/packages/llm/llm-retry/src/invariant.ts index 8655f6d661..afd1be1ccd 100644 --- a/packages/llm/llm-retry/src/invariant.ts +++ b/packages/llm/llm-retry/src/invariant.ts @@ -147,6 +147,7 @@ function validateStarted( /** Validate every retry record already present in one loaded 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 === 'llm/retry') validateRetry(events.slice(0, index), event, fail) @@ -161,7 +162,9 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant ctx.on('internal/dispatch', (_mode, eventName, args) => { if (eventName !== 'session/event') return const [session, event] = args as [Session, SessionEvent] + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. if (event.type === 'llm/retry') validateRetry(session.snapshotEvents(), event, fail) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. else if (event.type === 'llm/retry-started') validateStarted(session.snapshotEvents(), event, fail) }, { global: true }) }, { inject: ['sessions'] }) diff --git a/packages/llm/llm-retry/tests/loader-composition.spec.ts b/packages/llm/llm-retry/tests/loader-composition.spec.ts index 5c5a177040..7b95f6982d 100644 --- a/packages/llm/llm-retry/tests/loader-composition.spec.ts +++ b/packages/llm/llm-retry/tests/loader-composition.spec.ts @@ -112,6 +112,7 @@ describe('real Loader composition', () => { await agent.whenIdle() expect(adapter.requests).toBe(2) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'llm/retry')).toHaveLength(1) expect(agent.session.deriveMessages().at(-1)).toMatchObject({ role: 'assistant', diff --git a/packages/llm/llm-retry/tests/retry.spec.ts b/packages/llm/llm-retry/tests/retry.spec.ts index e7f0ededb2..cf61fcd4fa 100644 --- a/packages/llm/llm-retry/tests/retry.spec.ts +++ b/packages/llm/llm-retry/tests/retry.spec.ts @@ -215,6 +215,7 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(2) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(item => item.type === 'step/start').map(item => item.data)) .toEqual([{ turn: 1, step: 1 }]) expect(agent.session.deriveMessages().at(-1)).toEqual({ @@ -250,6 +251,7 @@ describe('provider-routed retry policy', () => { await idle 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 === 'assistant/message').map(event => ({ turn: event.data.turn, step: event.data.step, @@ -286,7 +288,9 @@ describe('provider-routed retry policy', () => { await vi.advanceTimersByTimeAsync(500) await idle + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const retryEvent = agent.session.snapshotEvents().find(event => event.type === 'llm/retry') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const failedAttempts = agent.session.snapshotEvents().filter((event): event is SessionEvent<'assistant/attempt'> => event.type === 'assistant/attempt' && retryEvent !== undefined @@ -294,12 +298,14 @@ describe('provider-routed retry policy', () => { ) expect(failedAttempts).toHaveLength(1) expect(expandAssistantStream(failedAttempts[0]!.data.stream)).toHaveLength(7) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const assistantMessages = agent.session.snapshotEvents().filter(event => event.type === 'assistant/message') expect(assistantMessages.map(event => ({ turn: event.data.turn, step: event.data.step, }))).toEqual([{ turn: 1, step: 1 }]) expect(assistantMessages[0]?.sourceEventSeqs).toBeUndefined() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'tool/call')).toBe(false) expect(toolExecutions).toBe(0) expect(agent.session.deriveMessages().at(-1)).toMatchObject({ @@ -337,7 +343,9 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(3) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'llm/retry')).toHaveLength(2) + // 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', error: { message: 'busy three', code: 'SERVER' } } }, @@ -393,6 +401,7 @@ describe('provider-routed retry policy', () => { rejectedAgent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } })) await rejectedIdle expect(rejected.requests).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(rejectedAgent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) }) @@ -431,6 +440,7 @@ describe('provider-routed retry policy', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } })) await idle expect(adapter.requests).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) expect(vi.getTimerCount()).toBe(0) }) @@ -450,7 +460,9 @@ describe('provider-routed retry policy', () => { await idle 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 === 'llm/retry')).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const end = agent.session.snapshotEvents().at(-1) expect(end).toMatchObject({ type: 'turn/end', @@ -479,6 +491,7 @@ describe('provider-routed retry policy', () => { const normalIdle = waitForIdle(context, normalAgent) normalAgent.followup(createUserMessage({ content: [{ type: 'text', text: 'normal' }], source: { kind: 'user' } })) await normalIdle + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(normalAgent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) const alwaysAgent = await context.agentLoop.create(SessionId('retry-provider-always'), { @@ -565,6 +578,7 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests.map(request => request.provider)).toEqual(['mock', 'other', 'other']) + // 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 => ({ provider: event.data.provider, retry: event.data.retry, @@ -676,6 +690,7 @@ describe('provider-routed retry policy', () => { await vi.runAllTimersAsync() await idle + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = agent.session.snapshotEvents().filter(event => event.type === 'llm/retry') expect(adapter.requests).toHaveLength(5) expect(events.map(event => ({ @@ -720,6 +735,7 @@ describe('provider-routed retry policy', () => { const retriedContext = JSON.stringify(adapter.requests[1]?.messages) expect(retriedContext).not.toContain(diagnostic) expect(retriedContext).not.toContain('discarded partial output') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry' && event.data.failure.message === diagnostic, )).toBe(true) @@ -742,6 +758,7 @@ describe('provider-routed retry policy', () => { await idle 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 === 'llm/retry')).toBe(false) }) @@ -793,6 +810,7 @@ describe('provider-routed retry policy', () => { await vi.advanceTimersByTimeAsync(60_000) 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 === 'step/start')).toHaveLength(1) expect(vi.getTimerCount()).toBe(0) }) @@ -834,6 +852,7 @@ describe('provider-routed retry policy', () => { expect(order[0]).toBe('downstream') expect(order).toEqual(expect.arrayContaining(['disposed', 'idle'])) expect(adapter.requests).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) }) @@ -872,6 +891,7 @@ describe('provider-routed retry policy', () => { expect(order).toEqual(['downstream', 'idle']) expect(adapter.requests).toHaveLength(1) + // 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' } }, @@ -909,6 +929,7 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(1) + // 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' } }, @@ -966,6 +987,7 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(1) + // 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' } }, @@ -995,7 +1017,9 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).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: 'aborted' } }, @@ -1019,6 +1043,7 @@ describe('provider-routed retry policy', () => { await idle 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 === 'llm/retry')).toHaveLength(1) expect(vi.getTimerCount()).toBe(0) }) diff --git a/packages/llm/llm-retry/tests/transport-recovery.spec.ts b/packages/llm/llm-retry/tests/transport-recovery.spec.ts index ba218842de..b14db6e47e 100644 --- a/packages/llm/llm-retry/tests/transport-recovery.spec.ts +++ b/packages/llm/llm-retry/tests/transport-recovery.spec.ts @@ -102,9 +102,11 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { expect(server).toBeDefined() expect(server?.requests).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') .map(event => [event.data.turn, event.data.step])) .toEqual([[1, 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.failure.code)) .toEqual(['TRANSPORT']) expect(finalAssistantText(agent)).toBe('connected after retry') @@ -131,7 +133,9 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { expect(server.requests).toHaveLength(2) expect(server.requests[0]?.body).toEqual(server.requests[1]?.body) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const retryEvent = agent.session.snapshotEvents().find(event => event.type === 'llm/retry') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const failedAttempts = agent.session.snapshotEvents().filter((event): event is SessionEvent<'assistant/attempt'> => event.type === 'assistant/attempt' && retryEvent !== undefined @@ -139,9 +143,11 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { ) expect(failedAttempts).toHaveLength(1) expect(expandAssistantStream(failedAttempts[0]!.data.stream)).toHaveLength(failedChunkCount) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'assistant/message') .map(event => [event.data.turn, event.data.step])) .toEqual([[1, 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.failure.code)) .toEqual(['TRANSPORT']) expect(finalAssistantText(agent)).toBe('recovered response') @@ -162,11 +168,14 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { expect(server.requests).toHaveLength(2) expect(server.requests[0]?.body).toEqual(server.requests[1]?.body) + // 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.failure.code)) .toEqual(['EMPTY_RESPONSE']) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'assistant/message') .map(event => [event.data.turn, event.data.step])) .toEqual([[1, 1]]) + // 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' } }, @@ -189,10 +198,14 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { await sendAndWait(context, agent) expect(server.requests).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const attempt = agent.session.snapshotEvents().find(event => event.type === 'assistant/attempt' && event.data.turn === 1) expect(attempt?.type === 'assistant/attempt' ? expandAssistantStream(attempt.data.stream) : []).toHaveLength(3) + // 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) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).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', error: { message: 'SSE stream ended without [DONE]', code: 'STREAM_CLOSED' } } }, @@ -215,6 +228,7 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { await sendAndWait(context, agent) expect(server.requests.map(record => record.behavior)).toEqual(['stall', 'success']) + // 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.failure.code)) .toEqual(['TIMEOUT']) expect(finalAssistantText(agent)).toBe('recovered after timeout') @@ -233,8 +247,11 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { await sendAndWait(context, agent) expect(server.requests).toHaveLength(3) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'step/start')).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')).toHaveLength(2) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const end = agent.session.snapshotEvents().at(-1) expect(end).toMatchObject({ type: 'turn/end', diff --git a/packages/llm/token-meter/src/index.ts b/packages/llm/token-meter/src/index.ts index 348213ef4d..2693af5913 100644 --- a/packages/llm/token-meter/src/index.ts +++ b/packages/llm/token-meter/src/index.ts @@ -229,7 +229,8 @@ export class TokenMeter extends Service { } while (state.consumedEvents < session.seq) { - // oxlint-disable-next-line typescript/no-non-null-assertion -- contiguous session seqs index the durable log + // Contiguous session seqs index the durable log; existing Session history read, migration deferred. + // oxlint-disable-next-line typescript/no-non-null-assertion, typescript/no-deprecated const event = session.eventAt(SessionSeq(state.consumedEvents))! this._foldEvent(state, event) state.consumedEvents = SessionLogOffset(state.consumedEvents + 1) diff --git a/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts b/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts index 769f00e0ec..35f64858f9 100644 --- a/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts +++ b/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts @@ -292,6 +292,7 @@ describe('contextBreakdown session projection', () => { const first = appendUser(session, 'first message') const last = appendUser(session, 'last message') const definition = contextBreakdownProjectionDefinition + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const state = session.snapshotEvents().reduce(definition.apply, definition.init()) const before = JSON.stringify(state) const replacement = session.append('user/message', createUserMessage({ @@ -382,8 +383,10 @@ describe('contextBreakdown session projection', () => { const { session } = await harness() const head = appendSystem(session, 'head') const definition = contextBreakdownProjectionDefinition + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const state = session.snapshotEvents().reduce(definition.apply, definition.init()) replaceSystem(session, head, 'same') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const next = definition.apply(state, session.snapshotEvents().at(-1)!) expect(next).not.toBe(state) expect(definition.wire.view(next)).toBe(definition.wire.view(state)) @@ -421,6 +424,7 @@ describe('contextBreakdown session projection', () => { stale, [replacement], SessionLogOffset(replacement.seq), session.header, session.inheritedEventCount, )).toThrow('re-read from seq 0') const replayed = ctx.sessionProjections.restore( + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. stale, session.snapshotEvents(), SessionLogOffset(0), session.header, session.inheritedEventCount, ) expect(replayed.snapshot.values.contextBreakdown).toEqual(projected(ctx, session)) @@ -433,6 +437,7 @@ describe('contextBreakdown session projection', () => { }, } expect(() => ctx.sessionProjections.restore( + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. invalid, session.snapshotEvents(), SessionLogOffset(0), session.header, session.inheritedEventCount, )).toThrow() }) @@ -452,6 +457,7 @@ describe('contextBreakdown session projection', () => { expect.soft(ctx.sessionProjections.viewCheckpoint(checkpoint)).not.toHaveProperty('contextBreakdown') expect.soft(ctx.sessionProjections.restoreFloor(checkpoint)).toBe(0) const restored = ctx.sessionProjections.restore( + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. checkpoint, session.snapshotEvents(), SessionLogOffset(0), session.header, session.inheritedEventCount, ) expect(restored.snapshot.values.contextBreakdown).toEqual({ diff --git a/packages/llm/token-meter/tests/token-meter.spec.ts b/packages/llm/token-meter/tests/token-meter.spec.ts index e795ea5507..bd8218beb0 100644 --- a/packages/llm/token-meter/tests/token-meter.spec.ts +++ b/packages/llm/token-meter/tests/token-meter.spec.ts @@ -230,6 +230,7 @@ describe('TokenMeter pricing', () => { const result = service.measure(session) expect(result.baseline.kind).toBe('estimated') expect(result.totalTokens).toBeGreaterThan(result.surfaceTokens) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(result.logRevision).toBe(session.snapshotEvents().length) expectSurfaceTotal(result) }) @@ -423,6 +424,7 @@ describe('replay anchors and surface folds', () => { content: [{ type: 'text', text: 'new tail' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seeded = Session.create(SessionId('surface-seeded'), original.snapshotEvents()) const before = service.measure(seeded) expect(before.nodes).toHaveLength(2) @@ -436,7 +438,9 @@ describe('replay anchors and surface folds', () => { }), { surfaceOp: { op: 'replace', startSeq: first, endSeq: first }, sourceEventSeqs: [first] }) const after = service.measure(seeded) expect(after.nodes).toHaveLength(2) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(after.nodes[0]!.seq).toBe(seeded.snapshotEvents().length - 1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(after.logRevision).toBe(seeded.snapshotEvents().length) expect(Object.isFrozen(after.nodes)).toBe(true) expect(Object.isFrozen(after.nodes[0])).toBe(true) @@ -444,6 +448,7 @@ describe('replay anchors and surface folds', () => { expectSurfaceTotal(after) expect(before.nodes).toHaveLength(2) // The earlier snapshot still reports the log it measured: seed + boundary. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(before.logRevision).toBe(original.snapshotEvents().length + 1) expect(before.surfaceDeltaTokens).toBeGreaterThan(0) }) @@ -455,6 +460,7 @@ describe('replay anchors and surface folds', () => { durableText: '', }) const measurement = meter().measure(session) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const assistant = session.snapshotEvents().find(event => event.type === 'assistant/message')! expect(measurement.nodes).toEqual([{ seq: assistant.seq, tokens: 0, heuristicTokens: 0 }]) expect(measurement.surfaceTokens).toBe(0) diff --git a/packages/plan/plan-mode/src/invariant.ts b/packages/plan/plan-mode/src/invariant.ts index 72a319fc45..60389c8879 100644 --- a/packages/plan/plan-mode/src/invariant.ts +++ b/packages/plan/plan-mode/src/invariant.ts @@ -28,6 +28,7 @@ function validateEvent(event: SessionEvent, fail: InvariantFailure): void { /** Install validation for loaded and newly appended plan-mode state. */ const install: InvariantInstaller = Object.assign((ctx: Context, fail: InvariantFailure) => { const seed = (session: Session): void => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) validateEvent(event, fail) } for (const session of ctx.sessions.list()) seed(session) diff --git a/packages/plan/plan-mode/tests/integration.spec.ts b/packages/plan/plan-mode/tests/integration.spec.ts index ac82e45822..4b0eb97f01 100644 --- a/packages/plan/plan-mode/tests/integration.spec.ts +++ b/packages/plan/plan-mode/tests/integration.spec.ts @@ -105,6 +105,7 @@ describe('plan mode through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'explore the repo' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() const planMode = findEvent(log, 'plan/mode') const header = findEvent(log, 'request/header') @@ -138,8 +139,10 @@ describe('plan mode through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'hello' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) expect(planActive(ctx, agent)).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const first = findEvent(agent.session.snapshotEvents(), 'request/header') expect(first.data.header.tools?.map(tool => tool.name)).toEqual(['exit_plan_mode', 'read', 'write']) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const firstSystem = findEvent(agent.session.snapshotEvents(), 'system/message') expect(systemText(agent)).not.toContain(PLAN_CONFIG.section) @@ -147,6 +150,7 @@ describe('plan mode through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'now plan' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() expect(planActive(ctx, agent)).toBe(true) const notices = log.filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin') @@ -196,6 +200,7 @@ describe('plan mode through the agent loop', () => { expect(requestSystem(adapter.requests[1])).not.toContain(PLAN_CONFIG.section) expect(adapter.requests[1]?.tools).toEqual(adapter.requests[0]?.tools) expect(ctx.planMode.get(agent)).toEqual({ active: false, pending: true }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) const nextIdle = waitForIdle(ctx, agent) @@ -205,6 +210,7 @@ describe('plan mode through the agent loop', () => { expect(adapter.requests).toHaveLength(3) expect(requestSystem(adapter.requests[2])).toContain(PLAN_CONFIG.section) expect(adapter.requests[2]?.tools).toEqual(adapter.requests[0]?.tools) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() const planMode = findEvent(log, 'plan/mode') const firstEnd = log.find(event => event.type === 'step/end' diff --git a/packages/plan/plan-mode/tests/plan-mode.spec.ts b/packages/plan/plan-mode/tests/plan-mode.spec.ts index 2e3bd89949..9bd0e7c87c 100644 --- a/packages/plan/plan-mode/tests/plan-mode.spec.ts +++ b/packages/plan/plan-mode/tests/plan-mode.spec.ts @@ -142,6 +142,7 @@ function header(session: Session): void { } function noticeTexts(session: Session): string[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents() .filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin') .map(event => (event.data as { content: { type: string; text?: string }[] }).content.map(block => block.text ?? '').join('')) @@ -205,10 +206,12 @@ describe('resolveConfig', () => { describe('foldPlanMode', () => { it('folds an empty log to inactive and takes the last plan/mode otherwise', () => { const session = Session.create(SessionId('fold')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents())).toBe(false) session.append('plan/mode', { active: true }) session.append('plan/mode', { active: false }) session.append('plan/mode', { active: true }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents())).toBe(true) }) @@ -216,7 +219,9 @@ describe('foldPlanMode', () => { const session = Session.create(SessionId('fold-prefix')) session.append('plan/mode', { active: true }) session.append('plan/mode', { active: false }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents(), 1)).toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents(), 0)).toBe(false) }) }) @@ -281,13 +286,16 @@ describe('ctx.planMode: get/set', () => { const ctx = await setup() const agent = await agentWithSession(ctx, 'agent-idle') expect(ctx.planMode.set(agent, true)).toBe('committed') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent)).toEqual({ active: true }) // Immediately reversible, still without a boundary. expect(ctx.planMode.set(agent, false)).toBe('committed') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) // A later boundary finds nothing pending — no double append. await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'plan/mode')).toHaveLength(2) }) @@ -299,6 +307,7 @@ describe('ctx.planMode: get/set', () => { closeTurn(agent.session) // Back to the logged state: the pending intent clears, nothing lands. expect(ctx.planMode.set(agent, false)).toBe('cancelled') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) expect(ctx.planMode.get(agent)).toEqual({ active: false }) }) @@ -319,6 +328,7 @@ describe('the boundary flush', () => { const service = ctx.planMode as unknown as { onBoundary(session: Session): void } expect(() => { service.onBoundary(agent.session) }).not.toThrow() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) }) @@ -328,6 +338,7 @@ describe('the boundary flush', () => { openTurn(agent.session) ctx.planMode.set(agent, true) await boundary(ctx, agent, 'pre-step') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent)).toEqual({ active: true }) }) @@ -343,6 +354,7 @@ describe('the boundary flush', () => { ctx.planMode.set(agent, true) await fiber.dispose() await boundary(ctx, agent, 'pre-step') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) }) @@ -351,6 +363,7 @@ describe('the boundary flush', () => { const agent = await agentWithSession(ctx) ctx.planMode.set(agent, true) await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -362,6 +375,7 @@ describe('the boundary flush', () => { ctx.planMode.set(agent, true) ctx.planMode.set(agent, false) await boundary(ctx, agent, 'pre-step') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) expect(noticeTexts(agent.session)).toEqual([]) }) @@ -403,6 +417,7 @@ describe('the boundary flush', () => { agent.session.append('plan/mode', { active: false }) ctx.planMode.set(agent, true) await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(noticeTexts(agent.session)).toEqual([]) }) @@ -430,6 +445,7 @@ describe('the boundary flush', () => { expect(ctx.planMode.get(agent)).toEqual({ active: false, pending: true }) agent.session.append = original await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent).pending).toBeUndefined() }) @@ -674,6 +690,7 @@ describe('/plan', () => { expect(enteringSteer).not.toHaveBeenCalled() await boundary(ctx, entering, 'step-start') expect(ctx.planMode.get(entering)).toEqual({ active: false }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(entering.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) const active = await agentWithSession(ctx, 'active-plan-command', { active: true }) @@ -698,9 +715,11 @@ describe('/plan', () => { const agent = await agentWithSession(ctx, 'idle-plan-command') expect((await ctx.commands.execute(agent, '/plan', [], signal))?.result) .toEqual({ kind: 'success', text: 'Plan mode on. Use /plan off to leave.' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect((await ctx.commands.execute(agent, '/plan off', [], signal))?.result) .toEqual({ kind: 'success', text: 'Plan mode off.' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) }) @@ -864,6 +883,7 @@ describe('exit_plan_mode', () => { expect(result.content).toEqual([{ type: 'text', text: 'Error: exit_plan_mode requires a non-empty markdown plan starting with a # heading' }]) } expect(asked).toHaveLength(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -873,6 +893,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: no user-questions channel is available to review the plan; ask the user to switch the session mode instead' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -881,6 +902,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: no user-questions answerer accepted the request' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -901,6 +923,7 @@ describe('exit_plan_mode', () => { text: "Error: human interaction is unavailable while the calling agent is owned by another live agent; include the unresolved question or decision in the child agent's final result", }]) expect(ask).not.toHaveBeenCalled() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(child.session.snapshotEvents())).toBe(true) }) @@ -913,9 +936,11 @@ describe('exit_plan_mode', () => { expect(result.content).toEqual([{ type: 'text', text: 'Plan approved — plan mode exited; carry out the plan starting with your next step.' }]) // Boundary-applied, not a direct append: the fold stays plan until the // step's end, so the plan policy covers any remaining call of the SAME batch. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent)).toEqual({ active: true, pending: false }) await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) expect(asked).toHaveLength(1) expect(asked[0]?.agent).toBe(agent) @@ -966,6 +991,7 @@ describe('exit_plan_mode', () => { question: 'Approve this plan and leave plan mode?', detail: plan, }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().find(event => event.type === 'tool/ptc-dispatch')?.data).toMatchObject({ name: EXIT_PLAN_MODE, arguments: { plan }, @@ -981,11 +1007,13 @@ describe('exit_plan_mode', () => { // Calls of the SAME assistant response were requested under the existing // plan-shaped header. Pending state shapes only the proposed next // assembly; the accepted boundary then commits the matching durable fold. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) const assembly = await ctx.systemPrompt.assemble({ agent }) expect(assembly.tools.some(tool => tool.name === EXIT_PLAN_MODE)).toBe(true) expect(assembly.sections.find(section => section.name === 'plan:policy')?.text).toBe('') await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) const afterExit = await ctx.systemPrompt.assemble({ agent }) expect(afterExit.tools).toEqual(assembly.tools) @@ -997,6 +1025,7 @@ describe('exit_plan_mode', () => { header(agent.session) await callExit(ctx, agent) await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) expect(noticeTexts(agent.session)).toEqual([]) }) @@ -1006,6 +1035,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; their feedback: consider the resume path' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1021,6 +1051,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; their feedback: add tests first' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1029,6 +1060,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; revise the plan and present it again.' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1037,6 +1069,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; their feedback: change the tests' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1051,6 +1084,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; revise the plan and present it again.' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1083,6 +1117,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user dismissed the plan review to speak instead; stay in plan mode, stop here, and wait for their message.' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1095,6 +1130,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: ask_user_question was aborted before the user answered' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1135,6 +1171,7 @@ describe('exit_plan_mode', () => { const result = await pending expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: the plan-mode service was reloaded while the plan was under review; present the plan again' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1144,6 +1181,7 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: review aborted' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1195,6 +1233,7 @@ describe('HMR disposal', () => { expect(ctx.tools.get(EXIT_PLAN_MODE)).toBeUndefined() expect((await ctx.systemPrompt.assemble()).sections.map(section => section.name)).not.toContain('plan:policy') await boundary(ctx, agent, 'step-start') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) }) }) diff --git a/packages/plan/plan-mode/tests/projection.spec.ts b/packages/plan/plan-mode/tests/projection.spec.ts index f4d46d5b62..1e15df94b4 100644 --- a/packages/plan/plan-mode/tests/projection.spec.ts +++ b/packages/plan/plan-mode/tests/projection.spec.ts @@ -155,6 +155,7 @@ describe('plan projection unit', () => { // A second registry over the same log (the cold-read shape): no service // memory involved, the fold alone answers {active:false, pending:true}. const cold = await harness(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of bench.session.snapshotEvents()) { if (event.type === 'command/run' || event.type === 'command/done' || event.type === 'plan/mode') { cold.session.append(event.type, event.data) diff --git a/packages/preset/agent-presets/tests/remote.spec.ts b/packages/preset/agent-presets/tests/remote.spec.ts index b1a685d019..f02651d2f5 100644 --- a/packages/preset/agent-presets/tests/remote.spec.ts +++ b/packages/preset/agent-presets/tests/remote.spec.ts @@ -98,6 +98,7 @@ async function agentOn(ctx: Context, id: string, presetId?: string): Promise + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. agent.session.snapshotEvents().findLast(event => event.type === 'agent-preset/selected')?.data describe('the roster a client reads', () => { diff --git a/packages/sandbox/sandbox-policy/src/invariant.ts b/packages/sandbox/sandbox-policy/src/invariant.ts index 6f3d5ca654..62ae2446ae 100644 --- a/packages/sandbox/sandbox-policy/src/invariant.ts +++ b/packages/sandbox/sandbox-policy/src/invariant.ts @@ -23,6 +23,7 @@ function validateEvent(event: SessionEvent, fail: InvariantFailure): void { /** Install validation for loaded and newly appended sandbox modes. */ const install: InvariantInstaller = Object.assign((ctx: Context, fail: InvariantFailure) => { for (const session of ctx.sessions.list()) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) validateEvent(event, fail) } ctx.on('internal/dispatch', (_mode, eventName, args) => { diff --git a/packages/sandbox/sandbox-policy/tests/policy.spec.ts b/packages/sandbox/sandbox-policy/tests/policy.spec.ts index 608abeeade..543fbff319 100644 --- a/packages/sandbox/sandbox-policy/tests/policy.spec.ts +++ b/packages/sandbox/sandbox-policy/tests/policy.spec.ts @@ -207,6 +207,7 @@ describe('sandbox:policy request context', () => { it('reconstructs resumed policy from the session log and omits diagnostics without an agent', async () => { const active = session('sess-resume', '/projects/current') setSandboxMode(active, 'workspace-write') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = Session.create(active.id, active.snapshotEvents(), active.header) const ctx = await promptMounted({ mode: 'read-only' }) @@ -232,6 +233,7 @@ describe('the sandbox/mode session kit', () => { it('setSandboxMode appends exactly one sandbox/mode event per switch', () => { const session = Session.create(SessionId('sess-write')) setSandboxMode(session, 'danger-full-access') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const modeEvents = session.snapshotEvents().filter(e => e.type === 'sandbox/mode') expect(modeEvents).toHaveLength(1) expect(modeEvents[0]?.data).toEqual({ mode: 'danger-full-access' }) diff --git a/packages/schedule/schedule/src/index.ts b/packages/schedule/schedule/src/index.ts index 9364fc53e0..7049246bdf 100644 --- a/packages/schedule/schedule/src/index.ts +++ b/packages/schedule/schedule/src/index.ts @@ -55,6 +55,7 @@ export function apply(ctx: Context): void { const cleanup: OwnerCleanup = agent.ctx.effect(() => { const disposeTools = registerScheduleTools(ctx, agent.ctx, agent, () => { runtime.requestDrive() }) const stopStatus = agent.ctx.on('agent/status', ({ status }) => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. if (status === 'idle' && agent.session.snapshotEvents().some(event => event.type === 'schedule/change')) { runtime.requestDrive() } diff --git a/packages/schedule/schedule/src/invariant.ts b/packages/schedule/schedule/src/invariant.ts index 248e7df710..0d5a89f926 100644 --- a/packages/schedule/schedule/src/invariant.ts +++ b/packages/schedule/schedule/src/invariant.ts @@ -30,15 +30,18 @@ function validate(events: readonly SessionEvent[], fail: InvariantFailure): void /** Install replay and pre-append validation for the owned event stream. */ const install: InvariantInstaller = Object.assign((ctx: Context, fail: InvariantFailure) => { for (const session of ctx.sessions.list()) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. validate(session.ownEvents(), fail) } ctx.on('session/created', (session) => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. validate(session.ownEvents(), fail) }, { global: true }) ctx.on('internal/dispatch', (_mode, eventName, args) => { if (eventName !== 'session/event') return const [session, event] = args as [Session, SessionEvent] if (event.type !== 'schedule/change') return + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. validate([...session.ownEvents(), event], fail) }, { global: true }) }, { inject: ['sessions'] }) diff --git a/packages/schedule/schedule/src/runtime.ts b/packages/schedule/schedule/src/runtime.ts index 5b141f5728..1574574c3d 100644 --- a/packages/schedule/schedule/src/runtime.ts +++ b/packages/schedule/schedule/src/runtime.ts @@ -205,6 +205,7 @@ export class ScheduleRuntime { /** Fold the current exact runtime suffix and contain a corrupt durable stream. */ private readFolded(): FoldedSchedules | undefined { try { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return foldScheduleEvents(this.agent.session.ownEvents()) } catch (error: unknown) { this.faulted = true diff --git a/packages/schedule/schedule/src/tools.ts b/packages/schedule/schedule/src/tools.ts index c4ac651634..83162ae2a9 100644 --- a/packages/schedule/schedule/src/tools.ts +++ b/packages/schedule/schedule/src/tools.ts @@ -221,6 +221,7 @@ function inputError(error: ScheduleInputError): ScheduleToolError { /** Fold only after a successful preflight, mapping corruption to a stable value. */ function foldForTool(agent: Agent): ReturnType | ScheduleToolError { try { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return foldScheduleEvents(agent.session.ownEvents()) } catch (error: unknown) { return error instanceof ScheduleLogError ? corruptLogError() : internalError() diff --git a/packages/schedule/schedule/tests/invariant.spec.ts b/packages/schedule/schedule/tests/invariant.spec.ts index f595bfa081..5600ea2635 100644 --- a/packages/schedule/schedule/tests/invariant.spec.ts +++ b/packages/schedule/schedule/tests/invariant.spec.ts @@ -53,6 +53,7 @@ describe('Schedule package invariant', () => { const session = ctx.sessions.create(SessionId('schedule-invariant')) session.append('turn/start', { turn: 1 }) session.append('schedule/change', create('schedule-1')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(2) expect(() => session.append('schedule/change', { @@ -60,9 +61,11 @@ describe('Schedule package invariant', () => { operation: 'delete', id: ScheduleId('missing'), })).toThrow(InvariantError) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(2) session.append('schedule/change', { version: 1, operation: 'dispatch', id: ScheduleId('schedule-1') }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(3) await ctx.fiber.dispose() }) @@ -82,6 +85,7 @@ describe('Schedule package invariant', () => { id: ScheduleId('schedule-every'), acceptedAt: '2026-08-05T12:17:34.000Z', }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(2) await ctx.fiber.dispose() }) @@ -118,6 +122,7 @@ describe('Schedule package invariant', () => { }) const fiber = await ctx.plugin(scheduleInvariant) child.append('schedule/change', create('child')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) await fiber.dispose() await ctx.fiber.dispose() diff --git a/packages/schedule/schedule/tests/jsonl-restart.spec.ts b/packages/schedule/schedule/tests/jsonl-restart.spec.ts index baf02e4702..3a86c940d6 100644 --- a/packages/schedule/schedule/tests/jsonl-restart.spec.ts +++ b/packages/schedule/schedule/tests/jsonl-restart.spec.ts @@ -107,6 +107,7 @@ describe('Schedule production JSONL restart', () => { ) pending.append('schedule/change', { version: 1, operation: 'create', schedule: pendingRecord }) const seed = await first.sessionPersistence.create(pending.header) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await seed.append(pending.snapshotEvents()) await seed.close() await disposeContext(first) @@ -143,6 +144,7 @@ describe('Schedule production JSONL restart', () => { await replayed.sessions.flush(replayHandle.agent.session) expect(replayAdapter.requests).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayHandle.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toHaveLength(1) const replayedStored = await readStored(replayed, sessionId) diff --git a/packages/schedule/schedule/tests/runtime.spec.ts b/packages/schedule/schedule/tests/runtime.spec.ts index f216babd7d..a1daefbef8 100644 --- a/packages/schedule/schedule/tests/runtime.spec.ts +++ b/packages/schedule/schedule/tests/runtime.spec.ts @@ -173,6 +173,7 @@ describe('Schedule timer and admission runtime', () => { await settle() expect(test.followed).toHaveLength(1) expect(test.controls.releaseCount).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().find(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toBeDefined() await runtime.dispose() @@ -223,6 +224,7 @@ describe('Schedule timer and admission runtime', () => { expect(test.followed).toEqual([]) expect(test.controls.whenIdleCount).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) runtime.requestDrive() @@ -295,12 +297,14 @@ describe('Schedule timer and admission runtime', () => { ].join('\n'), }]) expect(test.followed[0]?.source).toEqual({ kind: 'plugin', plugin: 'schedule' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const dispatches = test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch') expect(dispatches.map(event => event.data)).toEqual([ { version: 1, operation: 'dispatch', id: 'schedule-fast', acceptedAt: '2026-08-05T12:00:00.000Z' }, { version: 1, operation: 'dispatch', id: 'schedule-slow', acceptedAt: '2026-08-05T12:00:00.000Z' }, ]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldScheduleEvents(test.agent.session.snapshotEvents()).active).toEqual([ expect.objectContaining({ id: 'schedule-fast', scheduledAt: '2026-08-05T12:05:00.000Z' }), expect.objectContaining({ id: 'schedule-slow', scheduledAt: '2026-08-05T12:09:00.000Z' }), @@ -370,6 +374,7 @@ describe('Schedule timer and admission runtime', () => { expect(test.controls.releaseCount).toBe(1) expect(test.followed).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'delete' }) runtime.requestDrive() await settle() @@ -429,6 +434,7 @@ describe('Schedule runtime failure and teardown boundaries', () => { await settle() expect(test.controls.releaseCount).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toEqual([]) await runtime.dispose() @@ -460,6 +466,7 @@ describe('Schedule runtime failure and teardown boundaries', () => { expect(test.followed).toHaveLength(1) expect(test.controls.releaseCount).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toEqual([]) runtime.requestDrive() @@ -487,6 +494,7 @@ describe('Schedule runtime failure and teardown boundaries', () => { expect(test.followed).toHaveLength(1) expect(test.controls.releaseCount).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => ( event.type === 'schedule/change' && event.data.operation === 'dispatch' )).map(event => event.data)).toEqual([{ @@ -495,6 +503,7 @@ describe('Schedule runtime failure and teardown boundaries', () => { id: 'schedule-first', acceptedAt: '2026-08-05T12:00:00.000Z', }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldScheduleEvents(test.agent.session.snapshotEvents()).active).toEqual([ expect.objectContaining({ id: 'schedule-first', scheduledAt: '2026-08-05T12:05:00.000Z' }), expect.objectContaining({ id: 'schedule-second', scheduledAt: '2026-08-05T11:55:00.000Z' }), @@ -545,6 +554,7 @@ describe('Schedule runtime failure and teardown boundaries', () => { await settle() expect(test.controls.flushCount).toBe(1) expect(test.followed).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) await runtime.dispose() @@ -607,6 +617,7 @@ describe('Schedule runtime failure and teardown boundaries', () => { } await settle() expect(test.followed).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toEqual([]) }) diff --git a/packages/schedule/schedule/tests/tools.spec.ts b/packages/schedule/schedule/tests/tools.spec.ts index 35e4dc1724..467bc4f288 100644 --- a/packages/schedule/schedule/tests/tools.spec.ts +++ b/packages/schedule/schedule/tests/tools.spec.ts @@ -160,6 +160,7 @@ describe('Schedule tool protocol', () => { expect(value(await execute(test, 'schedule_create', { prompt: 'x', every_seconds: 299 }))) .toEqual({ code: 'frequency_too_high', message: 'every_seconds must be at least 300.' }) expect(test.flushes.count).toBe(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -233,6 +234,7 @@ describe('Schedule tool protocol', () => { expect.objectContaining({ id: 'schedule-1', kind: 'at' }), expect.objectContaining({ id: 'schedule-2', kind: 'at' }), ]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const changes = test.agent.session.snapshotEvents() .filter(event => event.type === 'schedule/change' && event.data.operation === 'create') expect(changes.map((change) => { @@ -301,6 +303,7 @@ describe('Schedule tool protocol', () => { message: 'The scheduled time must be strictly in the future.', }) expect(test.flushes.count).toBe(3) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -313,6 +316,7 @@ describe('Schedule tool protocol', () => { message: 'The scheduled time must be representable as a four-digit-year RFC 3339 UTC instant.', }) expect(test.flushes.count).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) const internal = await harness() @@ -439,6 +443,7 @@ describe('Schedule persistence failure boundaries', () => { error: { info: { name: 'AbortError', code: 'ABORTED' } }, }) expect(test.flushes.count).toBe(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -463,6 +468,7 @@ describe('Schedule persistence failure boundaries', () => { error: { info: { name: 'AbortError', code: 'ABORTED' } }, }) expect(test.flushes.count).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -486,6 +492,7 @@ describe('Schedule persistence failure boundaries', () => { error: { info: { name: 'AbortError', code: 'ABORTED' } }, }) expect(test.flushes.count).toBe(3) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')) .toHaveLength(1) expect(value(await execute(test, 'schedule_list', {}))) @@ -497,6 +504,7 @@ describe('Schedule persistence failure boundaries', () => { createTest.flushes.outcomes.push('reject') expect(value(await execute(createTest, 'schedule_create', { prompt: 'later', after_seconds: 1 }))) .toMatchObject({ code: 'persistence_uncertain', operation: 'create' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(createTest.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) const deleteTest = await harness() @@ -504,6 +512,7 @@ describe('Schedule persistence failure boundaries', () => { deleteTest.flushes.outcomes.push('reject') expect(value(await execute(deleteTest, 'schedule_delete', { id: 'schedule-1' }))) .toMatchObject({ code: 'persistence_uncertain', operation: 'delete', id: 'schedule-1' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(deleteTest.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) }) diff --git a/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts b/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts index 1d61e0ef3a..afb9125448 100644 --- a/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts +++ b/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts @@ -65,6 +65,7 @@ describe('session-log-download real Loader composition', () => { }) const execution = await context.commands.execute(agent, '/export', [], new AbortController().signal) expect(execution?.result).toEqual({ kind: 'success', text: 'Session log download requested.' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual(['command/run', 'command/done']) expect(session.deriveMessages()).toEqual([]) }) diff --git a/packages/session-query/session-query-sqlite/src/index.ts b/packages/session-query/session-query-sqlite/src/index.ts index 0fd390ee6a..e0b24b3c39 100644 --- a/packages/session-query/session-query-sqlite/src/index.ts +++ b/packages/session-query/session-query-sqlite/src/index.ts @@ -871,6 +871,7 @@ function selectedDocumentsParams(query: string, persistenceVisible: boolean): Ar } function observeLive(session: Session): ObservedSession { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return observeSession(session.header, session.inheritedEventCount, session.snapshotEvents()) } diff --git a/packages/session-query/session-query/src/corpus.ts b/packages/session-query/session-query/src/corpus.ts index a6e44c85ad..cd01ca8234 100644 --- a/packages/session-query/session-query/src/corpus.ts +++ b/packages/session-query/session-query/src/corpus.ts @@ -243,6 +243,7 @@ function projectSource( } function sourceLive(session: Session): LogicalSessionSource { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return { header: session.header, events: session.snapshotEvents() } } @@ -298,6 +299,7 @@ function snapshotLive(session: Session): LogicalSession { return { header: structuredClone(session.header), inheritedEventCount: session.inheritedEventCount, + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. events: session.snapshotEvents().map(event => structuredClone(event)), } } diff --git a/packages/session-query/session-query/src/observation.ts b/packages/session-query/session-query/src/observation.ts index 7ef86cebd4..8c08108775 100644 --- a/packages/session-query/session-query/src/observation.ts +++ b/packages/session-query/session-query/src/observation.ts @@ -289,6 +289,7 @@ export class SessionObservationReader { header: session.header, inheritedEventCount: session.inheritedEventCount, get events() { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. materialized ??= session.snapshotEvents(SessionLogOffset(0), seq) return materialized }, diff --git a/packages/session-query/session-query/tests/session-query.spec.ts b/packages/session-query/session-query/tests/session-query.spec.ts index ea89275ef3..b1cfa90c28 100644 --- a/packages/session-query/session-query/tests/session-query.spec.ts +++ b/packages/session-query/session-query/tests/session-query.spec.ts @@ -1034,6 +1034,7 @@ describe('session-query exact reads', () => { }).toThrow() Object.assign(snapshot.session, { cwd: '/mutated' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.eventAt(SessionSeq(4)) expect(logged?.type === 'user/message' && logged.data.content).toHaveLength(1) expect(session.header.cwd).toBe('/work') @@ -1071,6 +1072,7 @@ describe('session-query exact reads', () => { (result.events[0]!.data as { content: unknown[] }).content = [] }).toThrow() expect(session.header.createdAt).not.toBe(-1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.eventAt(SessionSeq(1)) expect(logged?.type === 'user/message' && logged.data.content).toHaveLength(1) diff --git a/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts b/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts index c127c2a127..0ca1de7052 100644 --- a/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts +++ b/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts @@ -2006,6 +2006,7 @@ describe('trace and exact read rendering', () => { expect(text(result)).toContain('Replacement chain: 1') expect(text(result)).toContain('Events cited directly as sources: none') expect(text(result)).toContain('Direct derived events: 1') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(text(result)).toContain(new Date(session.snapshotEvents()[0]?.time ?? 0).toISOString()) }) diff --git a/packages/session/session-log-deepseek/src/index.ts b/packages/session/session-log-deepseek/src/index.ts index 4c35021c8d..1147927bd5 100644 --- a/packages/session/session-log-deepseek/src/index.ts +++ b/packages/session/session-log-deepseek/src/index.ts @@ -120,6 +120,7 @@ export function acceptedThrough(session: Session): SessionSeqCursor { const length = session.seq const start = previous?.scannedEvents ?? SessionLogOffset(0) for (let index = start; index < length; index++) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.eventAt(SessionSeq(index)) if (event === undefined) { throw new Error(`session-log-deepseek: missing event ${String(index)} below captured length ${String(length)}`) @@ -164,9 +165,11 @@ export function apply(ctx: Context, config: Config): void { if (session === undefined) return undefined const afterSeq = acceptedThrough(session) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const snapshot = session.snapshotEvents() const throughSeq = snapshot.at(-1)?.seq if (throughSeq === undefined) return undefined + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const suffix = session.snapshotEvents(SessionLogOffset(afterSeq + 1)) const value: DeepSeekSessionLogExtension = { version: 1, diff --git a/packages/session/session-log-deepseek/src/invariant.ts b/packages/session/session-log-deepseek/src/invariant.ts index c855666ded..f01d5aef9e 100644 --- a/packages/session/session-log-deepseek/src/invariant.ts +++ b/packages/session/session-log-deepseek/src/invariant.ts @@ -44,6 +44,7 @@ function validateDeliveryAccepted(session: Session, event: SessionEvent<'session /** Validate acceptance watermarks 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. for (const event of session.snapshotEvents()) { if (event.type === 'session-log-deepseek/delivery-accepted') validateDeliveryAccepted(session, event, fail) } diff --git a/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts b/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts index 76702f4c38..31eb4d215f 100644 --- a/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts +++ b/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts @@ -84,6 +84,7 @@ it('uploads freeform feedback and message put/edit/delete through the unchanged recordFeedback(session, ' The session needs a clearer explanation. ') const created = await ctx.messageFeedback.put({ sessionId: session.id, messageId: assistant.id, rating: 'negative', note: 'Explain the result.', ifVersion: null }) if (!created.ok) throw new Error(created.error.code) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const initialPrefix = session.snapshotEvents() const request = async () => { const chunks = [] @@ -132,6 +133,7 @@ it('uploads freeform feedback and message put/edit/delete through the unchanged ] }) } await ctx.sessions.flush(session) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((await handle.read()).events).toEqual(session.snapshotEvents()) } finally { await handle.close() diff --git a/packages/session/session-log-deepseek/tests/upload.spec.ts b/packages/session/session-log-deepseek/tests/upload.spec.ts index e92df32921..dc4eb3f1c3 100644 --- a/packages/session/session-log-deepseek/tests/upload.spec.ts +++ b/packages/session/session-log-deepseek/tests/upload.spec.ts @@ -117,6 +117,7 @@ describe('incremental DeepSeek session-log upload', () => { turn: 1, step: 3, message: createSystemMessage('new head', 'fixture'), }, { surfaceOp: { op: 'replace', startSeq: head.seq, endSeq: head.seq }, sourceEventSeqs: [head.seq] }) const prepared = await ctx.deepseekLlmApiExtensions.prepare({ body: body(), signal: SIGNAL, sessionId: session.id }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(prepared.fields.dsh_session_log?.events).toEqual(session.snapshotEvents()) }) @@ -176,6 +177,7 @@ describe('incremental DeepSeek session-log upload', () => { expect(firstPayload?.events).toHaveLength(2) await first.accept() expect(SessionLogDeepSeek.acceptedThrough(session)).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().at(-1)?.data).toEqual({ sessionId: session.id, throughSeq: 1, @@ -197,6 +199,7 @@ describe('incremental DeepSeek session-log upload', () => { first.session.append('turn/start', { turn: 1 }) const prepared = await first.ctx.deepseekLlmApiExtensions.prepare({ body: body(), signal: SIGNAL, sessionId: first.session.id }) await prepared.accept() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = first.session.snapshotEvents() const resumed = await harness('parent', seed) @@ -361,6 +364,7 @@ describe('incremental DeepSeek session-log upload', () => { const { ctx, session } = await harness('direct-events') session.append('turn/start', { turn: 1 }) const prepared = await ctx.deepseekLlmApiExtensions.prepare({ body: {}, signal: SIGNAL, sessionId: session.id }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(prepared.fields.dsh_session_log?.events).toEqual(session.snapshotEvents()) }) diff --git a/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts b/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts index 9be35ec1bf..814da947a6 100644 --- a/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts +++ b/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts @@ -176,6 +176,7 @@ function assertMigrated(result: Awaited>) { expect(session.firstLiveSeq).toBe(23) expect(session.isOwnSeq(SessionSeq(8))).toBe(false) expect(session.isOwnSeq(SessionSeq(9))).toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.ownEvents()).toEqual([ ...events.slice(9), expect.objectContaining({ type: 'session/end-seed', seq: 23, data: {} }), diff --git a/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts b/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts index b8a7fad132..3c40b96b20 100644 --- a/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts +++ b/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts @@ -131,6 +131,7 @@ describe('V2 system prompts through current Session and JSONL persistence', () = const session = await restore(reader) expect(messages(session)).toEqual([human]) expect(session.surface.nodes).toEqual([8, 3]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(8))?.type).toBe('system/message') } finally { await reader.close() @@ -207,6 +208,7 @@ describe('V2 system prompts through current Session and JSONL persistence', () = expect(session.firstLiveSeq).toBe(15) expect(session.surface.nodes).toEqual([4, 8]) expect(messages(session)).toEqual([prompt('seed prompt'), { role: 'user', content: [{ type: 'text', text: 'summary' }] }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.ownEvents()).toEqual([prepared[14]]) } finally { await reader.close() @@ -227,7 +229,9 @@ describe('V2 system prompts through current Session and JSONL persistence', () = }, { surfaceOp: { op: 'replace', startSeq: SessionSeq(4), endSeq: SessionSeq(4) }, sourceEventSeqs: [SessionSeq(4)] }) session.append('step/end', { turn: 2, step: 1 }) session.append('turn/end', { turn: 2, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expected = session.snapshotEvents() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await writer.append(session.snapshotEvents(session.firstLiveSeq)) await writer.flush() } finally { @@ -286,6 +290,7 @@ describe('V2 system prompts through current Session and JSONL persistence', () = }, { surfaceOp: 'append' }) session.append('step/end', { turn: 1, step: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await writer.append(session.snapshotEvents()) await writer.flush() } finally { @@ -294,6 +299,7 @@ describe('V2 system prompts through current Session and JSONL persistence', () = const reopened = await mount() const reader = await reopened.sessionPersistence.open(id, 'read') try { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((await reader.read()).events).toEqual(session.snapshotEvents()) const restored = await restore(reader) expect(restored.surface.nodes).toEqual([2, 3, 4]) diff --git a/packages/session/session-projection/src/index.ts b/packages/session/session-projection/src/index.ts index d29c5bb021..6d28631ddc 100644 --- a/packages/session/session-projection/src/index.ts +++ b/packages/session/session-projection/src/index.ts @@ -619,6 +619,7 @@ export class SessionProjectionRegistry extends Service { registration.def, session.header, session.inheritedEventCount, + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. session.snapshotEvents(), ) registration.cells.set(session, cell) @@ -637,6 +638,7 @@ export class SessionProjectionRegistry extends Service { ): void { if (cell.observedSeq >= throughSeq) return for (let seq = cell.observedSeq + 1; seq <= throughSeq; seq++) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.eventAt(SessionSeq(seq)) if (event === undefined || event.seq !== seq) { throw new Error(`session projection ${JSON.stringify(def.key)} cannot advance across missing seq ${String(seq)}`) @@ -663,6 +665,7 @@ export class SessionProjectionRegistry extends Service { registration.def, session.header, session.inheritedEventCount, + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. session.snapshotEvents(SessionLogOffset(0), SessionLogOffset(event.seq)), ) registration.cells.set(session, cell) diff --git a/packages/session/session-telemetry-otel/src/index.ts b/packages/session/session-telemetry-otel/src/index.ts index 43c93fb8ec..0722845a3b 100644 --- a/packages/session/session-telemetry-otel/src/index.ts +++ b/packages/session/session-telemetry-otel/src/index.ts @@ -246,6 +246,7 @@ export class OpenTelemetrySessionBackend extends SessionTelemetryBackend { ctx.on('session/event', (session, event) => { if (!isFeedback(session, event)) return // Only the canonical appended event authorizes this exact prefix. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. if (session.eventAt(event.seq) !== event) { ctx.logger.warn(NON_CANONICAL_EVENT_WARNING) return diff --git a/packages/session/session-telemetry-otel/tests/otel.spec.ts b/packages/session/session-telemetry-otel/tests/otel.spec.ts index b8919024bd..35c2b89e11 100644 --- a/packages/session/session-telemetry-otel/tests/otel.spec.ts +++ b/packages/session/session-telemetry-otel/tests/otel.spec.ts @@ -175,6 +175,7 @@ describe('OpenTelemetrySessionBackend wire', () => { const start = ledger.find(r => r.record.attributes?.some(a => a.key === 'event.type' && a.value.stringValue === 'turn/start')) expect(start).toBeDefined() expect(start?.record.severityNumber).toBe(9) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(BigInt(start!.record.timeUnixNano)).toBe(BigInt(session.snapshotEvents().find(event => event.type === 'turn/start')!.time) * 1_000_000n) expect(start?.record.attributes).toContainEqual({ key: 'session.format_version', @@ -240,6 +241,7 @@ describe('OpenTelemetrySessionBackend wire', () => { }, }) expect(eventTypes(captures)).not.toContain('manual') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) expect(ops).toHaveLength(0) }) @@ -493,6 +495,7 @@ describe('OpenTelemetrySessionBackend wire', () => { session.append('turn/start', { turn: 1 }) expect(captures).toEqual([]) recordFeedback(session, 'explicit report') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const submitted = session.snapshotEvents().map(event => event.type) await expect.poll(() => eventTypes(captures)).toEqual(submitted) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) @@ -518,6 +521,7 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { session.append('turn/start', { turn: 1 }) expect(captures).toEqual([]) recordFeedback(session, 'explicit report') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const expected = session.snapshotEvents().map(event => event.type) await expect.poll(() => eventTypes(captures)).toEqual(expected) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) @@ -535,6 +539,7 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { await ctx.plugin(SessionStore) const donor = Session.create(SessionId('stored-feedback')) recordFeedback(donor, 'old feedback is not a submission') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const restored = ctx.sessions.create(donor.id, { seed: donor.snapshotEvents(), meta: donor.header }) try { const first = await ctx.plugin(OpenTelemetrySessionBackend, { mode: SessionTelemetryMode.FEEDBACK_ONLY, exporter: { url } }) @@ -545,8 +550,10 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { session.append('request/header', { header: { config: { provider, model: 'm' } }, reason: 'change' }) } const child = ctx.sessions.fork(restored, undefined, SessionId('child')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const inherited = child.snapshotEvents().find(event => event.type === 'feedback/record')! ctx.emit('session/event', child, inherited) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const opened = ctx.sessions.create(SessionId('opened'), { seed: donor.snapshotEvents() }) ctx.emit('session/created', opened) await first.dispose() @@ -581,6 +588,7 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { expect(captures).toEqual([]) const put = await ctx.messageFeedback.put(request) if (!put.ok) throw new Error('live put failed') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await expect.poll(() => eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) const throughPut = session.seq await ctx.messageFeedback.put({ ...request, ifVersion: put.value.version }) @@ -588,8 +596,10 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { expect(session.seq).toBe(throughPut) const edit = await ctx.messageFeedback.put({ ...request, ifVersion: put.value.version, note: 'edited note' }) if (!edit.ok) throw new Error('live note failed') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await expect.poll(() => eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) await ctx.messageFeedback.delete({ sessionId: session.id, messageId: message.id, ifVersion: edit.value.version }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await expect.poll(() => eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) const submitted = eventTypes(captures) const throughDelete = session.seq @@ -612,6 +622,7 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { const { ctx, fiber } = await boot(url) const session = Session.create(SessionId('cold-not-submitted')) const notify = async () => ctx.parallel('feedback/committed', { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. meta: session.header, events: session.snapshotEvents(), inheritedEventCount: session.inheritedEventCount, }) await notify() @@ -623,6 +634,7 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { child.append('feedback/message-delete', { sessionId: session.id, messageId: message.id }) await ctx.parallel('feedback/committed', { meta: { ...session.header, id: SessionId('inherited-cold'), parentSession: session.id, isSeeded: true }, + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. events: session.snapshotEvents(SessionLogOffset(0), SessionLogOffset(1)), inheritedEventCount: SessionLogOffset(1), }) await fiber.dispose() @@ -642,12 +654,14 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { parent.append('request/header', { header: { config: { provider: 'mock', model: 'm' } }, reason: 'initial' }) const message = createAssistantMessage({ content: [{ type: 'text', text: 'inherited answer' }], source: { provider: 'mock', model: 'm' } }) parent.append('assistant/message', { message, stream: [], turn: 1, step: 1 }, { surfaceOp: 'append' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = Session.create(SessionId('cold-child'), parent.snapshotEvents(), { ...parent.header, id: SessionId('cold-child'), parentSession: parent.id, isSeeded: true, }, parent.seq) recordFeedback(child, 'child-owned stored feedback') const handle = await ctx.sessionPersistence.create(child.header, { inheritedEventCount: child.inheritedEventCount }) try { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await handle.append(child.snapshotEvents()) } finally { await handle.close() @@ -695,6 +709,7 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { session.append('assistant/message', { message, stream: [], turn: 1, step: 1 }, { surfaceOp: 'append' }) const handle = await ctx.sessionPersistence.create(session.header) try { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await handle.append(session.snapshotEvents()) } finally { await handle.close() diff --git a/packages/session/session-telemetry/src/coordinator.ts b/packages/session/session-telemetry/src/coordinator.ts index 773aecb923..14c9916c64 100644 --- a/packages/session/session-telemetry/src/coordinator.ts +++ b/packages/session/session-telemetry/src/coordinator.ts @@ -153,6 +153,7 @@ export class SessionTelemetryCoordinator { ?? (this.options.includeHistory === true || session.firstLiveSeq === 0 ? -1 : SessionSeq(session.firstLiveSeq - 1)) // Containment is PER EVENT: one rejected record is withheld fail-closed // while the rest of the historical replay proceeds. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents(SessionLogOffset(cursor + 1))) { if (throughSeq !== undefined && event.seq > throughSeq) break this.contain(() => { diff --git a/packages/session/session-telemetry/tests/redact.spec.ts b/packages/session/session-telemetry/tests/redact.spec.ts index b563d9c469..7ecdc624a0 100644 --- a/packages/session/session-telemetry/tests/redact.spec.ts +++ b/packages/session/session-telemetry/tests/redact.spec.ts @@ -72,6 +72,7 @@ describe('session-telemetry/record waterfall', () => { session.append('user/message', createUserMessage({ content: [{ type: 'text', text: FIXTURE_SECRET }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[0]!.data as { content: { text: string }[] } expect(logged.content[0]!.text).toBe(FIXTURE_SECRET) }) @@ -124,6 +125,7 @@ describe('session-telemetry/record waterfall', () => { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) expect(backend.records).toHaveLength(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(1) }) }) diff --git a/packages/session/session-telemetry/tests/telemetry.spec.ts b/packages/session/session-telemetry/tests/telemetry.spec.ts index ac9ffdcfc0..8a5b50bbe7 100644 --- a/packages/session/session-telemetry/tests/telemetry.spec.ts +++ b/packages/session/session-telemetry/tests/telemetry.spec.ts @@ -128,11 +128,13 @@ describe('SessionTelemetryCoordinator capture', () => { 'event.type': 'turn/start', 'event.seq': 0, }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(start.time).toBe(session.snapshotEvents()[0]!.time) expect(start.severity).toBe('info') expect(message.attributes['event.seq']).toBe(1) // Deep-copy isolation: mutating the handed-off body never reaches the log. ;(message.body as { content: { text: string }[] }).content[0]!.text = 'tampered' + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[1] as SessionEvent<'user/message'> expect(logged.data.content[0]).toMatchObject({ text: 'hello' }) }) @@ -244,6 +246,7 @@ describe('SessionTelemetryCoordinator on-demand capture', () => { const session = liveSession(ctx, 'on-demand-prefix') appendTurn(session) appendAssistantMessage(session, 1, 1, ['first'], 100) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const firstBoundary = session.snapshotEvents()[2]!.seq appendAssistantMessage(session, 1, 2, ['second'], 200) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) @@ -299,6 +302,7 @@ describe('SessionTelemetryCoordinator on-demand capture', () => { }) const parent = liveSession(ctx, 'history-parent') appendTurn(parent) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = ctx.sessions.create(SessionId('history-child'), { seed: [...parent.snapshotEvents()] }) const boundary = child.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) child.append('turn/start', { turn: 2 }) @@ -398,6 +402,7 @@ describe('SessionTelemetryCoordinator adoption', () => { inject: ['sessions'], apply: (inner: Context) => void new SessionTelemetryCoordinator(inner, backend), }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = ctx.sessions.prepare(SessionId('seeded'), { seed: [...parent.snapshotEvents()], meta: {} }) child.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) ctx.sessions.enter(child) @@ -427,6 +432,7 @@ describe('SessionTelemetryCoordinator adoption', () => { // telemetry therefore receives a current-format object with the complete // migrated canonical seed. const resumed = ctx.sessions.prepare(SessionId('resumed'), { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: structuredClone(donor.snapshotEvents()) as SessionEvent[], meta: { version: SESSION_FORMAT_VERSION, @@ -460,7 +466,9 @@ describe('SessionTelemetryCoordinator adoption', () => { const parent = liveSession(ctx, 'stitch-parent') appendTurn(parent) const child = ctx.sessions.create(SessionId('stitch-child'), { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: [...parent.snapshotEvents()], + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. inheritedEventCount: SessionLogOffset(parent.snapshotEvents().length), meta: { parentSession: SessionId('stitch-parent'), isSeeded: true }, }) diff --git a/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts b/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts index 429f1f5bd3..ef37a9d131 100644 --- a/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts +++ b/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts @@ -53,6 +53,7 @@ describe('all-messages LLM title provider', () => { ctx.llm.registerAdapter(['current-route'], adapter) await ctx.plugin(providerPlugin, LLM_CONFIG) const session = ctx.sessions.create(SessionId('all-plugin'), { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: seeded.snapshotEvents(), inheritedEventCount: seeded.seq, meta: { parentSession: seeded.id, isSeeded: true }, diff --git a/packages/session/session-title-llm/tests/llm.spec.ts b/packages/session/session-title-llm/tests/llm.spec.ts index 240ddefa2b..bd9ce65dc4 100644 --- a/packages/session/session-title-llm/tests/llm.spec.ts +++ b/packages/session/session-title-llm/tests/llm.spec.ts @@ -126,6 +126,7 @@ describe('generateSessionTitleWithLlm', () => { const providerRequest = request(ctx) let requestWasLoggedAtDispatch = false const adapter = new RecordingAdapter(SCRIPT, () => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. requestWasLoggedAtDispatch = providerRequest.session.snapshotEvents() .some(event => event.type === 'session/title-llm-request') }) @@ -162,6 +163,7 @@ describe('generateSessionTitleWithLlm', () => { const prompt = options.messages[0]?.content[0] expect(prompt?.type === 'text' && prompt.text).toContain('first prompt') expect(prompt?.type === 'text' && prompt.text).toContain('第二个问题') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(providerRequest.session.snapshotEvents().findLast(event => event.type === 'session/title-llm-request')?.data) .toEqual({ titleProvider: TITLE_PROVIDER, @@ -193,6 +195,7 @@ describe('generateSessionTitleWithLlm', () => { await expect(generateSessionTitleWithLlm(ctx, config, oversized, [selected], TITLE_PROVIDER)) .rejects.toThrow(/input.*bytes.*maxInputBytes/i) expect(adapter.requests).toEqual([]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(oversized.session.snapshotEvents().some(event => event.type === 'session/title-llm-request')).toBe(false) const withinLimit = resolveSessionTitleLlmConfig({ ...config, maxInputBytes: 1_000 }) @@ -261,6 +264,7 @@ describe('generateSessionTitleWithLlm', () => { providerRequest.messages, TITLE_PROVIDER, )).rejects.toMatchObject({ message, code }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(providerRequest.session.snapshotEvents().some(event => event.type === 'session/title-llm-request')).toBe(true) }) diff --git a/packages/session/session-title/src/index.ts b/packages/session/session-title/src/index.ts index 3e36f1d588..014310b529 100644 --- a/packages/session/session-title/src/index.ts +++ b/packages/session/session-title/src/index.ts @@ -383,6 +383,7 @@ export class SessionTitleService extends Service { * @returns latest title snapshot, or `undefined` before eligible input. */ get(session: Session): SessionTitleSnapshot | undefined { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return foldSessionTitle(session.snapshotEvents()) } @@ -593,6 +594,7 @@ export class SessionTitleService extends Service { this.assertCurrent(session, work) await this.ensureFallback(session) this.assertCurrent(session, work) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const messages = collectSessionTitleMessages(session.snapshotEvents(), work.throughSeq) const result = await work.registration.provider.generate({ session, diff --git a/packages/session/session-title/src/invariant.ts b/packages/session/session-title/src/invariant.ts index e12cd6ca25..986b95f81d 100644 --- a/packages/session/session-title/src/invariant.ts +++ b/packages/session/session-title/src/invariant.ts @@ -46,6 +46,7 @@ function validate( fail(`session/title event ${String(event.seq)} repeats message seq ${checked}`) } seen.add(checked) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const cited = checked < event.seq ? session.eventAt(checked) : undefined if (cited?.type !== 'user/message' || cited.data.source.kind !== 'user') { fail(`session/title event ${String(event.seq)} message seq ${checked} must name an earlier human user/message`) @@ -55,6 +56,7 @@ function validate( const install: InvariantInstaller = Object.assign((ctx: Context, fail: InvariantFailure) => { const validateExisting = (session: Session): void => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { if (event.type === 'session/title') validate(session, event, fail) } diff --git a/packages/session/session-title/tests/projection.spec.ts b/packages/session/session-title/tests/projection.spec.ts index df2b5f1f91..90f6a207bb 100644 --- a/packages/session/session-title/tests/projection.spec.ts +++ b/packages/session/session-title/tests/projection.spec.ts @@ -17,6 +17,7 @@ async function harness(withTitleService: boolean): Promise<{ ctx: Context; sessi } function appendTitle(session: Session, title: string): SessionSeqType { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const messageSeq = session.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'user')?.seq ?? session.append('user/message', createUserMessage({ diff --git a/packages/session/session-title/tests/provider.spec.ts b/packages/session/session-title/tests/provider.spec.ts index d9632ea3b6..2708d51e2c 100644 --- a/packages/session/session-title/tests/provider.spec.ts +++ b/packages/session/session-title/tests/provider.spec.ts @@ -66,7 +66,9 @@ describe('SessionTitleService Provider lifecycle', () => { const child = ctx.sessions.fork(parent, undefined, SessionId('title-child')) expect(ctx.sessionTitle.get(child)).toEqual(ctx.sessionTitle.get(parent)) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.snapshotEvents().find(event => event.type === 'session/title')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. .toEqual(parent.snapshotEvents().find(event => event.type === 'session/title')) const firstGenerate = vi.fn(async (request: SessionTitleProviderRequest) => ({ @@ -377,6 +379,7 @@ describe('SessionTitleService Provider lifecycle', () => { }))) await settle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'request/header')).toHaveLength(1) expect(requests).toHaveLength(2) expect(requests[1]).toMatchObject({ diff --git a/packages/session/session-title/tests/rename.spec.ts b/packages/session/session-title/tests/rename.spec.ts index 9654c18edc..706511d2e3 100644 --- a/packages/session/session-title/tests/rename.spec.ts +++ b/packages/session/session-title/tests/rename.spec.ts @@ -46,6 +46,7 @@ describe('SessionTitleService.rename', () => { messageSeqs: [], source: { kind: 'user' }, }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents().findLast(item => item.type === 'session/title') expect(event?.data).toEqual({ title: 'Hand picked name', @@ -53,6 +54,7 @@ describe('SessionTitleService.rename', () => { source: { kind: 'user' }, }) // foldSessionTitle round-trips the third source kind. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSessionTitle(session.snapshotEvents())?.source).toEqual({ kind: 'user' }) }) @@ -164,6 +166,7 @@ describe('SessionTitleService.rename', () => { await settle() // The released provider result must not append over the user title, and // the swallowed abort must not surface as an unhandled rejection. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const latest = session.snapshotEvents().findLast(item => item.type === 'session/title') expect(latest?.data).toMatchObject({ title: 'User wins', source: { kind: 'user' } }) }) diff --git a/packages/session/session-title/tests/service-contracts.spec.ts b/packages/session/session-title/tests/service-contracts.spec.ts index c6e0dc93c3..c0b13910fe 100644 --- a/packages/session/session-title/tests/service-contracts.spec.ts +++ b/packages/session/session-title/tests/service-contracts.spec.ts @@ -171,6 +171,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => { }) const source = appendPrompt(seed, 'Create exactly one fallback title') seed.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const session = ctx.sessions.create(SessionId('fallback-concurrency'), { seed: seed.snapshotEvents() }) const results = await Promise.all([ @@ -179,7 +180,9 @@ describe('SessionTitleService configuration and refresh boundaries', () => { ]) expect(results[0]).toEqual(results[1]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'session/title')).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual([ 'turn/start', 'user/message', @@ -204,6 +207,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => { }) await expect(refresh).resolves.toMatchObject({ title: 'Already accepted' }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'session/title')).toHaveLength(1) }) @@ -266,6 +270,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => { await fiber.dispose() await settle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().some(event => event.type === 'session/title')).toBe(false) const inactiveError = await lifecycle.inactiveRefresh expect(inactiveError).toBeInstanceOf(Error) @@ -285,6 +290,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => { await Promise.resolve() await fiber.dispose() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().some(event => event.type === 'session/title')).toBe(false) expect(warn).not.toHaveBeenCalled() }) diff --git a/packages/session/session-title/tests/session-title.spec.ts b/packages/session/session-title/tests/session-title.spec.ts index 69edbb84ff..b5cbf31c3c 100644 --- a/packages/session/session-title/tests/session-title.spec.ts +++ b/packages/session/session-title/tests/session-title.spec.ts @@ -53,6 +53,7 @@ describe('SessionTitleService', () => { await settleTitles() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const titleEvent = session.snapshotEvents().findLast(event => event.type === 'session/title') expect(titleEvent).toMatchObject({ type: 'session/title', @@ -140,6 +141,7 @@ describe('SessionTitleService', () => { expect(first?.messageSeqs).toEqual([eligible.seq]) expect(ctx.sessionTitle.get(session)).toEqual(first) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'session/title')).toHaveLength(1) }) @@ -160,6 +162,7 @@ describe('SessionTitleService', () => { }, }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSessionTitle(seed.snapshotEvents())).toEqual({ title: 'Later', messageSeqs: [1, 4], @@ -169,6 +172,7 @@ describe('SessionTitleService', () => { model: { provider: 'mock', model: 'title-model' }, }, eventSeq: 1, + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. updatedAt: seed.snapshotEvents()[1]?.time, }) }) @@ -176,6 +180,7 @@ describe('SessionTitleService', () => { it('folds an empty or title-less log to undefined', () => { expect(foldSessionTitle([])).toBeUndefined() const empty = Session.create(SessionId('no-title')) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSessionTitle(empty.snapshotEvents())).toBeUndefined() }) }) diff --git a/packages/shell/tool-bash/tests/integration.spec.ts b/packages/shell/tool-bash/tests/integration.spec.ts index cf2a4e1d2e..d561bed780 100644 --- a/packages/shell/tool-bash/tests/integration.spec.ts +++ b/packages/shell/tool-bash/tests/integration.spec.ts @@ -58,6 +58,7 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { } function events(agent: Agent): readonly SessionEvent[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() } diff --git a/packages/shell/tool-bash/tests/tools.spec.ts b/packages/shell/tool-bash/tests/tools.spec.ts index 1dfbd27b81..b70b02691c 100644 --- a/packages/shell/tool-bash/tests/tools.spec.ts +++ b/packages/shell/tool-bash/tests/tools.spec.ts @@ -631,6 +631,7 @@ describe('sandbox escalation through the generic task producer', () => { expect(prompted).not.toHaveBeenCalled() const malformed = sandboxAgent() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. ;(malformed.session.snapshotEvents() as unknown as Array<{ type: string; data: { mode: string }; seq: number }>).push({ type: 'sandbox/mode', data: { mode: 'unknown-mode' }, diff --git a/packages/skill/tool-skill/src/index.ts b/packages/skill/tool-skill/src/index.ts index 3465cd843c..0099332fa2 100644 --- a/packages/skill/tool-skill/src/index.ts +++ b/packages/skill/tool-skill/src/index.ts @@ -362,6 +362,7 @@ function catalogHistory(agent: Agent): { visibleDigest?: string; published: bool const visible = new Set(agent.session.surface.nodes) let published = false for (let index = agent.session.seq - 1; index >= 0; index -= 1) { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = agent.session.eventAt(SessionSeq(index)) if (event === undefined) { throw new Error(`skill catalog cannot read seq ${String(index)} below the current Session length`) diff --git a/packages/skill/tool-skill/tests/tool-skill.spec.ts b/packages/skill/tool-skill/tests/tool-skill.spec.ts index 603166de04..7fe649b1a8 100644 --- a/packages/skill/tool-skill/tests/tool-skill.spec.ts +++ b/packages/skill/tool-skill/tests/tool-skill.spec.ts @@ -124,6 +124,7 @@ async function proposeStep( } function catalogMessages(session: Session): Extract[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents().filter((event): event is Extract => event.type === 'user/message' && event.data.source.kind === 'skill-catalog') } diff --git a/packages/subagent/subagent-fork-in-process/src/index.ts b/packages/subagent/subagent-fork-in-process/src/index.ts index 0e0311ebb5..0e69915b60 100644 --- a/packages/subagent/subagent-fork-in-process/src/index.ts +++ b/packages/subagent/subagent-fork-in-process/src/index.ts @@ -46,6 +46,7 @@ export const Config: z = z.object({ * @returns the seed events, contiguous from seq 0; empty when no turn has completed. */ function completedTurnPrefix(parent: Agent): SessionEvent[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = parent.session.snapshotEvents() const lastEnd = events.findLast(e => e.type === 'turn/end') if (lastEnd === undefined) return [] diff --git a/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts b/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts index 35c8af4df3..04238d479b 100644 --- a/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts +++ b/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts @@ -67,6 +67,7 @@ describe('multi-subagent coexistence (spawn + fork on one context)', () => { // Parent does one real turn first, so the fork has a completed turn to seed. parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent q1' }], source: { kind: 'user' } })) await parent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentPrefixLen = parent.session.snapshotEvents().length // Delegate to a fresh spawn child. @@ -88,6 +89,7 @@ describe('multi-subagent coexistence (spawn + fork on one context)', () => { expect(spawnChild.session.header.parentSession).toBe(parent.session.header.id) expect(forkChild.session.header.parentSession).toBe(parent.session.header.id) // The fork child inherited the parent's prefix; the spawn child did not. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(forkChild.session.snapshotEvents().slice(0, parentPrefixLen).some(e => e.type === 'user/message')).toBe(true) await spawnRun.dispose() @@ -96,12 +98,14 @@ describe('multi-subagent coexistence (spawn + fork on one context)', () => { // The parent is unaffected and keeps working after both delegations. parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent q2' }], source: { kind: 'user' } })) await parent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const lastParentMessage = parent.session.snapshotEvents().findLast(e => e.type === 'assistant/message') expect(lastParentMessage?.type === 'assistant/message' && text(lastParentMessage.data.message.content)).toBe('parent turn two') // The parent's OWN log never recorded the children's internal steps — its // only subagent-related entries would be tool/call+tool/result IF it had // used the tool, but here we called the service directly, so the parent log // is purely its own two turns. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().filter(e => e.type === 'turn/end')).toHaveLength(2) }) }) diff --git a/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts b/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts index 6f7080cd31..2e557bebb0 100644 --- a/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts +++ b/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts @@ -84,6 +84,7 @@ describe('dsh-subagent-fork-in-process', () => { expect(text(result.output)).toBe('fresh child') const child = ctx.agents.get(run.id)! // Only the child's own turn — no seeded parent turns. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(e => e.type === 'turn/end')).toHaveLength(1) expect(child.session.header.isSeeded).toBe(false) expect(child.session.inheritedEventCount).toBe(0) @@ -96,6 +97,7 @@ describe('dsh-subagent-fork-in-process', () => { await parent.whenIdle() parent.followup(createUserMessage({ content: [{ type: 'text', text: 'q2' }], source: { kind: 'user' } })) await parent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentPrefixLen = parent.session.snapshotEvents().length const run = await start(ctx, 'fork', { prompt: [{ type: 'text', text: 'child q' }], parent }) @@ -103,7 +105,9 @@ describe('dsh-subagent-fork-in-process', () => { const child = ctx.agents.get(run.id)! expect(child.session.header.isSeeded).toBe(true) expect(child.session.inheritedEventCount).toBe(parentPrefixLen) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, parentPrefixLen).at(-1)?.type).toBe('turn/end') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, parentPrefixLen).filter(e => e.type === 'turn/end')).toHaveLength(2) await run.dispose() }) @@ -112,6 +116,7 @@ describe('dsh-subagent-fork-in-process', () => { const { ctx, parent } = await setup([textResponse('parent answer'), textResponse('child answer')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent question' }], source: { kind: 'user' } })) await parent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentPrefixLen = parent.session.snapshotEvents().length const run = await start(ctx, 'fork', { prompt: [{ type: 'text', text: 'child question' }], parent }) @@ -121,8 +126,10 @@ describe('dsh-subagent-fork-in-process', () => { const child = ctx.agents.get(run.id)! // The child's log STARTS with the parent's prefix (seeded), then its own turn. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().length).toBeGreaterThan(parentPrefixLen) // The seeded prefix carried the parent's user message. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seededUser = child.session.snapshotEvents().slice(0, parentPrefixLen).find(e => e.type === 'user/message') expect(seededUser).toBeDefined() // Lineage stamped. @@ -153,6 +160,7 @@ describe('dsh-subagent-fork-in-process', () => { const child = ctx.agents.get(run.id)! // The child's seed has exactly the ONE completed parent turn (the open one excluded). + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seedTurnEnds = child.session.snapshotEvents().filter(e => e.type === 'turn/end') // 1 from the seeded parent turn + 1 from the child's own completed turn. expect(seedTurnEnds.length).toBe(2) diff --git a/packages/subagent/subagent-in-process-driver/src/index.ts b/packages/subagent/subagent-in-process-driver/src/index.ts index c0886dc104..91348b2da9 100644 --- a/packages/subagent/subagent-in-process-driver/src/index.ts +++ b/packages/subagent/subagent-in-process-driver/src/index.ts @@ -215,6 +215,7 @@ function readResult( cancelled: boolean, structured?: { captured?: { value: unknown } | undefined }, ): SubagentResult { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const own = child.session.snapshotEvents(boundary) // `droppedUnrun` is deliberately unread: a one-shot prompt is claimed by its // awaited first turn almost immediately, and the owner's own teardown is the diff --git a/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts b/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts index d3fc067a5a..72d3426a8a 100644 --- a/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts @@ -69,6 +69,7 @@ function spawnRequest(parent: Agent) { } function toolResultTexts(agent: Agent): string[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() .filter((event): event is SessionEvent<'tool/result'> => event.type === 'tool/result') .map(event => event.data.message.content @@ -86,6 +87,7 @@ describe('in-process policy inheritance', () => { setSandboxMode(parent.session, 'read-only') // No parent approval override: the child pin must not depend on one. expect(ctx.approval.overrideOf(parent.session)).toBeUndefined() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentLogLength = parent.session.snapshotEvents().length script.push( toolCallResponse('write', 'write', { file_path: blocked, content: 'escaped' }), @@ -100,6 +102,7 @@ describe('in-process policy inheritance', () => { await expect(readFile(blocked, 'utf8')).rejects.toMatchObject({ code: 'ENOENT' }) expect(toolResultTexts(child).join('\n')).toContain(READ_ONLY_DENIAL) expect(result.stopReason).toBe('completed') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, 2)).toMatchObject([ { type: 'sandbox/mode', seq: 0, data: { mode: 'read-only', source: 'delegation' } }, { type: 'approval/policy', seq: 1, data: { policy: 'never', source: 'delegation' } }, @@ -109,12 +112,15 @@ describe('in-process policy inheritance', () => { expect(child.session.inheritedEventCount).toBe(0) expect(ctx.sandboxPolicy.overrideOf(child.session)).toBe('read-only') expect(ctx.approval.overrideOf(child.session)).toBe('never') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const request = child.session.snapshotEvents().find( (event): event is SessionEvent<'request/header'> => event.type === 'request/header', ) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const systemNode = child.session.snapshotEvents().find( (event): event is SessionEvent<'system/message'> => event.type === 'system/message', ) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const runtimeContext = child.session.snapshotEvents().find( (event): event is SessionEvent<'user/message'> => event.type === 'user/message' && event.data.source.kind === 'plugin' @@ -142,6 +148,7 @@ describe('in-process policy inheritance', () => { .join('\n') expect(systemText).not.toContain('Approval prompts are disabled') expect(systemText).not.toContain('You are a delegated subagent') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents()).toHaveLength(parentLogLength) } finally { await run.dispose() @@ -153,6 +160,7 @@ describe('in-process policy inheritance', () => { const { ctx, parent } = await setupWalled(script) const blocked = join(workspace, 'fork-blocked.txt') setSandboxMode(parent.session, 'workspace-write') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = parent.session.snapshotEvents() setSandboxMode(parent.session, 'read-only') script.push( @@ -169,6 +177,7 @@ describe('in-process policy inheritance', () => { expect(child.session.inheritedEventCount).toBe(1) expect(child.session.firstLiveSeq).toBe(seed.length) // seq 1 is the constructor's end-seed marker. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(event => event.type === 'sandbox/mode')).toMatchObject([ { seq: 0, data: { mode: 'workspace-write' } }, { seq: 2, data: { mode: 'read-only', source: 'delegation' } }, @@ -215,7 +224,9 @@ describe('in-process policy inheritance', () => { await run.result const child = run.localAgent as Agent expect(await readFile(allowed, 'utf8')).toBe('fine') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().some(event => event.type === 'sandbox/mode')).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(event => event.type === 'approval/policy')).toMatchObject([ { seq: 0, data: { policy: 'never', source: 'delegation' } }, ]) @@ -255,9 +266,11 @@ describe('in-process policy inheritance', () => { expect(consulted).toBe(false) expect(toolResultTexts(child).join('\n')) .toContain('the user rejected escalating this operation to "workspace-write"') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const asked = child.session.snapshotEvents().find( (event): event is SessionEvent<'approval/asked'> => event.type === 'approval/asked', ) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decided = child.session.snapshotEvents().find( (event): event is SessionEvent<'approval/decided'> => event.type === 'approval/decided', ) diff --git a/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts b/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts index 106b9e3eaa..e4ec2605b0 100644 --- a/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts @@ -85,6 +85,7 @@ describe('a child agent composed in-process', () => { const run = await startInProcessRun(spawnRequest(parent), {}) await run.result + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(run.localAgent?.session.snapshotEvents().some(event => event.type === 'system/message' && JSON.stringify(event.data.message.content).includes('section for preset_only'))).toBe(true) diff --git a/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts b/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts index fa4bfdb22b..70398a2f9f 100644 --- a/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts @@ -196,6 +196,7 @@ describe('in-process structured output', () => { expect(result.structured).toEqual({ answer: 5 }) expect(sideEffectRan).toBe(false) const child = ctx.agents.get(run.id) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const sideEffectResult = child?.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.source.callId === 'c2') expect(sideEffectResult?.type === 'tool/result' && sideEffectResult.data.message.content[0].isError).toBe(true) @@ -240,6 +241,7 @@ describe('in-process structured output', () => { expect(result.stopReason).toBe('completed') // The child's log carries the isError tool/result for the invalid call. const child = ctx.agents.get(run.id)! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = child.session.snapshotEvents().filter(e => e.type === 'tool/result') expect(results.length).toBe(2) expect(results[0]!.data.message.content[0].isError).toBe(true) @@ -258,6 +260,7 @@ describe('in-process structured output', () => { // Exactly one model request and one caller-supplied user message: no nudge turn exists. expect(adapter.requests.length).toBe(1) const child = ctx.agents.get(run.id)! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(e => e.type === 'user/message' && e.data.source.kind !== 'plugin').length).toBe(1) await run.dispose() }) @@ -323,6 +326,7 @@ describe('in-process structured output', () => { expect(result.stopReason).toBe('error') // ...the logged tool result is the blocked isError with the feedback... const child = ctx.agents.get(run.id)! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = child.session.snapshotEvents().filter(e => e.type === 'tool/result') expect(results[0]!.data.message.content[0].isError).toBe(true) expect(JSON.stringify(results[0]!.data.message.content)).toContain('capture rejected by hook') @@ -368,6 +372,7 @@ describe('in-process structured output', () => { expect(result.structured).toBeUndefined() expect(result.stopReason).toBe('error') const child = ctx.agents.get(run.id) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const captureResult = child?.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.source.callId === 'c1') expect(captureResult?.type === 'tool/result' && captureResult.data.message.content[0].isError).toBe(true) @@ -440,6 +445,7 @@ describe('in-process structured output', () => { expect(result.stopReason).toBe('error') expect(adapter.requests).toHaveLength(2) const child = ctx.agents.get(run.id)! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const outer = child.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.source.callId === ToolCallId('c1')) expect(outer?.type === 'tool/result' && outer.data.message.content[0].isError).toBe(true) diff --git a/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts b/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts index d11fa73b05..e274ac7cb4 100644 --- a/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts @@ -150,6 +150,7 @@ describe('startInProcessRun', () => { let injected = false ctx.on('session/flush', (session) => { if (injected || session.header.parentSession === undefined) return + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const lastEnd = session.snapshotEvents().findLast(event => event.type === 'turn/end') if (lastEnd?.type !== 'turn/end' || lastEnd.data.reason.kind !== 'max-tokens') return injected = true @@ -164,6 +165,7 @@ describe('startInProcessRun', () => { const child = ctx.agents.get(run.id)! expect(injected).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().findLast(event => event.type === 'turn/end')) .toMatchObject({ data: { reason: { kind: 'max-tokens' } } }) expect(result.stopReason).toBe('max-tokens') @@ -199,6 +201,7 @@ describe('startInProcessRun', () => { const { ctx, parent } = await setup([textResponse('parent answer'), textResponse('child answer')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent question' }], source: { kind: 'user' } })) await parent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = parent.session.snapshotEvents() const run = await startInProcessRun(request(parent), { seed }) const result = await run.result @@ -206,11 +209,13 @@ describe('startInProcessRun', () => { const child = ctx.agents.get(run.id)! expect(child.session.header.isSeeded).toBe(true) expect(child.session.inheritedEventCount).toBe(seed.length) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, seed.length)).toEqual(seed) // The seeded `system/message` stays surface node 0: the child renders the // same prompt, so its loop appends no second system node. const seededSystem = seed.find(event => event.type === 'system/message') expect(seededSystem).toBeDefined() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(event => event.type === 'system/message')).toHaveLength(1) expect(child.session.surface.nodes[0]).toBe(seededSystem?.seq) expect(child.session.deriveMessages()[0]).toEqual(parent.session.deriveMessages()[0]) @@ -221,6 +226,7 @@ describe('startInProcessRun', () => { const { ctx, parent, adapter } = await setup([textResponse('parent answer'), textResponse('child answer')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent question' }], source: { kind: 'user' } })) await parent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = parent.session.snapshotEvents() const seededSystem = seed.find(event => event.type === 'system/message') if (seededSystem === undefined) throw new Error('the parent log lacks a system node') @@ -229,6 +235,7 @@ describe('startInProcessRun', () => { const run = await startInProcessRun(request(parent), { seed }) await run.result const child = ctx.agents.get(run.id)! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const systemNodes = child.session.snapshotEvents().filter(event => event.type === 'system/message') expect(systemNodes.map(event => event.seq)).toEqual([seededSystem.seq, systemNodes[1]?.seq]) expect(systemNodes[1]?.surfaceOp).toEqual({ op: 'replace', startSeq: seededSystem.seq, endSeq: seededSystem.seq }) @@ -360,6 +367,7 @@ describe('startInProcessRun', () => { }) expect(adapter.requests[0]?.signal?.reason).toEqual({ kind: 'parent' }) const child = parent.ctx.agents.get(signalled.id) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEnd = child?.session.snapshotEvents().findLast(event => event.type === 'turn/end') expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason).toEqual({ kind: 'aborted', reason: { kind: 'parent' } }) await signalled.dispose() diff --git a/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts b/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts index 3e764070f7..c031772384 100644 --- a/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts +++ b/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts @@ -38,6 +38,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('spawn backend with-key smoke', ( // The parent's log records the subagent tool/call + its result (not the // child's internal steps). + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = parent.session.snapshotEvents() const subagentCalls = events.filter(e => e.type === 'tool/call' && e.data.name === 'subagent') expect(subagentCalls.length).toBeGreaterThan(0) diff --git a/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts b/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts index b678f5c263..957496e492 100644 --- a/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts +++ b/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts @@ -118,6 +118,7 @@ describe('dsh-subagent-spawn-in-process', () => { const { ctx, parent } = await setup([textResponse('parent turn'), textResponse('child sees nothing')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent prompt' }], source: { kind: 'user' } })) await parent.whenIdle() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentEventCount = parent.session.snapshotEvents().length expect(parentEventCount).toBeGreaterThan(0) @@ -125,6 +126,7 @@ describe('dsh-subagent-spawn-in-process', () => { await run.result const child = ctx.agents.get(run.id)! // The child's first user/message is its OWN prompt, not the parent's history. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const firstUser = child.session.snapshotEvents().find(e => e.type === 'user/message') expect(firstUser).toBeDefined() await run.dispose() @@ -439,6 +441,7 @@ describe('dsh-subagent-spawn-in-process', () => { expect((childRequest.tools ?? []).map(t => t.name)).not.toContain('forbidden_tool') // …and the attempted call executed as UNKNOWN_TOOL (visible in the log). const child = ctx.agents.get(run.id)! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const toolResult = child.session.snapshotEvents().find(e => e.type === 'tool/result')! expect(JSON.stringify(toolResult.data)).toContain('unknown tool') await run.dispose() diff --git a/packages/subagent/subagent/src/lifecycle.ts b/packages/subagent/subagent/src/lifecycle.ts index ba2db8e62c..adeba106f6 100644 --- a/packages/subagent/subagent/src/lifecycle.ts +++ b/packages/subagent/subagent/src/lifecycle.ts @@ -198,6 +198,7 @@ export function createActivationObserver( emit('subagent/start', identity, parent) }, capture: (child: Agent): void => { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const own = child.session.snapshotEvents(boundary) const output = finalAssistantOutput(own) captured = { diff --git a/packages/subagent/subagent/tests/continuation.spec.ts b/packages/subagent/subagent/tests/continuation.spec.ts index f2bbd16ef5..ecaa72a347 100644 --- a/packages/subagent/subagent/tests/continuation.spec.ts +++ b/packages/subagent/subagent/tests/continuation.spec.ts @@ -258,6 +258,7 @@ describe('SubagentRuntime.startContinuable', () => { ctx.on('agent/inbox/inserted', ({ agent, message }) => { // Acceptance is the boundary `startContinuable` resolves at, so observe // the log state exactly there rather than after later microtasks. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. enqueued.push({ id: message.id, loggedYet: hasUserText(agent.session.snapshotEvents(), 'child task') }) }) @@ -450,6 +451,7 @@ describe('SubagentRuntime.startContinuable', () => { })).rejects.toBe(catalogFailure) await vi.waitFor(() => { expect(ctx.agents.get(childId)).toBeUndefined() }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().filter(event => event.type === 'subagent/catalog')).toEqual([]) }) @@ -529,6 +531,7 @@ describe('SubagentRuntime.startContinuable', () => { expect(found).toBeDefined() return found! }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const descriptor = child.session.snapshotEvents().find(event => event.type === 'subagent/descriptor') expect(descriptor?.data).toEqual({ @@ -564,6 +567,7 @@ describe('SubagentRuntime.startContinuable', () => { return found! }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().find(event => event.type === 'subagent/descriptor')?.data) .toEqual({ version: SUBAGENT_DESCRIPTOR_VERSION, @@ -1155,6 +1159,7 @@ describe('continuable durability and teardown', () => { child.followup(createUserMessage({ content: message('accepted during flush'), source: { kind: 'user' } })) await vi.waitFor(() => { expect(adapter.requests).toHaveLength(2) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(hasAssistantText(child.session.snapshotEvents(), 'late answer')).toBe(true) }) await child.whenIdle() @@ -1882,6 +1887,7 @@ describe('continuable review regressions', () => { const started = await ctx.subagents.startContinuable(startSpec(parent)) await vi.waitFor(() => { expect(adapter.requests).toHaveLength(1) }) const child = ctx.agents.get(started.childId)! + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = child.session.snapshotEvents().length const controller = new AbortController() @@ -2343,6 +2349,7 @@ describe('continuable review regressions', () => { /** Every settlement notice this agent received, in order, as flat text. */ function settlementNotices(agent: Agent): { sender: string; text: string; summary: string }[] { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = agent.session.snapshotEvents().flatMap(event => event.type === 'user/message' ? [event.data] : []) return [...logged, ...agent.inbox.nextStep, ...agent.inbox.nextTurn].flatMap((message) => { if (message.source.kind !== 'subagent-settled') return [] @@ -2405,6 +2412,7 @@ describe('continuable adjacent-Agent delivery', () => { await vi.waitFor(() => { expect(adapter.requests.filter(request => request.sessionId === parent.id)).toHaveLength(1) }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const delivered = parent.session.snapshotEvents().flatMap(event => event.type === 'user/message' && event.data.source.kind === 'agent-message' ? [event.data] : [])[0] expect(delivered?.id).toBe(messageId) @@ -2699,6 +2707,7 @@ describe('continuable settlement delivery', () => { // Turn 1 closed cleanly and no later turn opened, so the cancelled queue is // the only record that this epoch was cut short. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(hasUserText(child.session.snapshotEvents(), 'never runs')).toBe(false) await vi.waitFor(() => { expect(settlementNotices(parent)).toHaveLength(1) }) expect(settlementNotices(parent)[0]!.text).toBe( @@ -2867,7 +2876,9 @@ describe('continuable settlement delivery', () => { `Background subagent ${started.childId} was stopped before it finished.` + '\nIt left no closing message.', ) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().some(event => event.type === 'agent/inbox/spliced')).toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(false) expect(parent.status).toBe('idle') }) @@ -2884,6 +2895,7 @@ describe('continuable settlement delivery', () => { await drained expect(settlementNotices(parent)).toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(false) }) diff --git a/packages/subagent/tool-subagent/src/index.ts b/packages/subagent/tool-subagent/src/index.ts index 5ca770d7d7..4e8cc4b6b1 100644 --- a/packages/subagent/tool-subagent/src/index.ts +++ b/packages/subagent/tool-subagent/src/index.ts @@ -619,6 +619,7 @@ export function apply(ctx: Context, config: Config, session?: Session): void { } const selectForSession = (target: Session): ModelSelectionPolicy | undefined => { const freshSession = target.firstLiveSeq === 0 + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. && target.eventAt(SessionSeq(0))?.type !== 'session/end-seed' let allowedModels = subagentModelSelectionPolicy(ctx.sessionProjections, target) if (allowedModels === undefined) { diff --git a/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts b/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts index f5921a3fab..2af3149716 100644 --- a/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts +++ b/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts @@ -347,6 +347,7 @@ describe('SubagentModelSelectionConfig', () => { const enabledSeed = Session.create(SessionId('enabled-seed')) enabledSeed.append('subagent/model-selection-policy', { allowedModels: ALLOWED_MODELS }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumedEnabled = await createAgent(ctx, 'resumed-enabled', { seed: enabledSeed.snapshotEvents() }) expect(selectable(ctx, resumedEnabled)).toBe(true) @@ -359,6 +360,7 @@ describe('SubagentModelSelectionConfig', () => { expect(selectable(ctx, resumedEmpty)).toBe(false) expect(subagentModelSelectionPolicy(ctx.sessionProjections, resumedEmpty.session)).toBeUndefined() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumedDisabled = await createAgent(ctx, 'resumed-disabled', { seed: oldSeed.snapshotEvents() }) expect(selectable(ctx, resumedDisabled)).toBe(false) expect(subagentModelSelectionPolicy(ctx.sessionProjections, resumedDisabled.session)).toBeUndefined() diff --git a/packages/terminal/terminal-bash/tests/index.spec.ts b/packages/terminal/terminal-bash/tests/index.spec.ts index af707d8269..78216805a3 100644 --- a/packages/terminal/terminal-bash/tests/index.spec.ts +++ b/packages/terminal/terminal-bash/tests/index.spec.ts @@ -618,6 +618,7 @@ describe('terminal-bash plugin shape', () => { expect(() => { setSandboxMode(session, 'read-only') }).toThrow( 'cannot change sandbox mode from "danger-full-access" to "read-only" while persistent terminal sessions are open or being created; wait for creation to settle and close them first', ) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'sandbox/mode')).toHaveLength(1) const replacementFiber = await registerStubLocalBackend(ctx, () => stubLocalSession()) @@ -628,6 +629,7 @@ describe('terminal-bash plugin shape', () => { await ctx.terminals.kill(owner, created.sessionId) await ctx.terminals.kill(owner, second.sessionId) expect(() => { setSandboxMode(session, 'read-only') }).not.toThrow() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'sandbox/mode')).toHaveLength(2) }) diff --git a/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts b/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts index f4235aad18..f65454023a 100644 --- a/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts +++ b/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts @@ -96,6 +96,7 @@ describe('dsh-agent-loop-testkit', () => { { id: step.id, turn: 3 }, { id: turn.id, turn: 3 }, ]) + // 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', 'agent/inbox/spliced', diff --git a/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts b/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts index f80929880d..31292f374c 100644 --- a/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts +++ b/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts @@ -19,11 +19,13 @@ export function apply(ctx: Context): void { || exec.arguments.file_path !== 'nested/task.txt') return downstream const agent = exec.agent const baseline = agent.session.surface.nodes + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. .map(seq => agent.session.snapshotEvents()[seq]) .find(event => event?.type === 'user/message' && event.data.source.kind === 'agent-instructions' && event.data.source.baseline === true) if (baseline === undefined) throw new Error('workspace baseline missing before snapshot compaction') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const openTurn = agent.session.snapshotEvents().findLast(event => event.type === 'turn/start') if (openTurn?.type !== 'turn/start') throw new Error('workspace snapshot compaction has no open turn') const compactionId = CompactionId('workspace-context-fixture') diff --git a/packages/todo/tool-todo/src/invariant.ts b/packages/todo/tool-todo/src/invariant.ts index 21970cbcb9..d92563a06f 100644 --- a/packages/todo/tool-todo/src/invariant.ts +++ b/packages/todo/tool-todo/src/invariant.ts @@ -60,6 +60,7 @@ function validateEvent(event: SessionEvent, trace: TurnTrace, fail: InvariantFai /** Validate one existing log in a single pass and return its tail trace. */ function seedTrace(session: Session, fail: InvariantFailure): TurnTrace { const trace: TurnTrace = { open: false } + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { validateEvent(event, trace, fail) advanceTrace(trace, event) diff --git a/packages/todo/tool-todo/tests/integration.spec.ts b/packages/todo/tool-todo/tests/integration.spec.ts index 3384372a65..cde02c8482 100644 --- a/packages/todo/tool-todo/tests/integration.spec.ts +++ b/packages/todo/tool-todo/tests/integration.spec.ts @@ -63,6 +63,7 @@ describe('todo_write tool through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'plan a two-step task' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() expect(findEvent(log, 'tool/call').data.name).toBe('todo_write') expect(findEvent(log, 'tool/result').data.message.content[0].isError).toBe(false) @@ -91,8 +92,10 @@ describe('todo_write tool through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'plan then update' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const todoEvents = agent.session.snapshotEvents().filter(e => e.type === 'todo/write') expect(todoEvents).toHaveLength(2) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(findEvent(agent.session.snapshotEvents(), 'todo/write', 'last').data.todos).toEqual([ { content: 'step one', status: 'completed' }, { content: 'step two', status: 'in_progress' }, diff --git a/packages/todo/tool-todo/tests/invariant.spec.ts b/packages/todo/tool-todo/tests/invariant.spec.ts index 4b41b6a301..0501982bdb 100644 --- a/packages/todo/tool-todo/tests/invariant.spec.ts +++ b/packages/todo/tool-todo/tests/invariant.spec.ts @@ -66,9 +66,11 @@ describe('todo snapshot invariants', () => { const session = ctx.sessions.create() session.append('turn/start', { turn: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents() expect(() => session.append('todo/write', { todos: [] })).toThrow(/outside any open turn/) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual(before) }) diff --git a/packages/todo/tool-todo/tests/loader-composition.spec.ts b/packages/todo/tool-todo/tests/loader-composition.spec.ts index b99909fa2a..039ba4dc1f 100644 --- a/packages/todo/tool-todo/tests/loader-composition.spec.ts +++ b/packages/todo/tool-todo/tests/loader-composition.spec.ts @@ -112,6 +112,7 @@ describe('tool-todo real Loader composition through cordis.yml', () => { }) expect(result.isError).toBe(true) expect(resultText(result)).toContain('at most one task may be in_progress') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().some(e => e.type === 'todo/write')).toBe(false) }, 30_000) @@ -129,6 +130,7 @@ describe('tool-todo real Loader composition through cordis.yml', () => { agent: owner, }) expect(result.isError).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().findLast(e => e.type === 'todo/write')?.data.todos).toEqual(PARALLEL_TODOS) }, 30_000) diff --git a/packages/todo/tool-todo/tests/tool-todo.spec.ts b/packages/todo/tool-todo/tests/tool-todo.spec.ts index a3d60f5b63..b045128eac 100644 --- a/packages/todo/tool-todo/tests/tool-todo.spec.ts +++ b/packages/todo/tool-todo/tests/tool-todo.spec.ts @@ -82,6 +82,7 @@ describe('dsh-tool-todo', () => { }) expect(text(result)).toContain('1 pending, 1 in progress, 0 completed') + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')! expect(event.data.todos).toEqual(todos) }) @@ -92,6 +93,7 @@ describe('dsh-tool-todo', () => { const result = await callTodo(ctx, { todos: [{ content: ' plan the work ', status: 'pending' }] }, { agent }) expect(result.isError).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')! expect(event.data.todos).toEqual([{ content: 'plan the work', status: 'pending' }]) }) @@ -105,6 +107,7 @@ describe('dsh-tool-todo', () => { { content: 'b', status: 'in_progress' }, ] }, { agent }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const current = agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos expect(current).toEqual([ { content: 'a', status: 'completed' }, @@ -139,6 +142,7 @@ describe('dsh-tool-todo', () => { todos, counts: { pending: 1, inProgress: 2, completed: 0 }, }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos).toEqual(todos) }) @@ -155,6 +159,7 @@ describe('dsh-tool-todo', () => { expect(result.isError).toBe(true) expect(text(result)).toContain('at most one task may be in_progress') // A rejected call must not reach the durable log. + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(e => e.type === 'todo/write')).toBe(false) }) @@ -247,6 +252,7 @@ describe('todo/write event', () => { ] session.append('todo/write', { todos }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents().findLast(e => e.type === 'todo/write')! expect(event.type).toBe('todo/write') expect(event.data.todos).toEqual(todos) @@ -268,6 +274,7 @@ describe('todo/write event', () => { { content: 'second', status: 'in_progress' }, ] }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const current = session.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos expect(current).toEqual([ { content: 'first', status: 'completed' }, @@ -293,10 +300,13 @@ describe('todo/write event', () => { original.append('turn/start', { turn: 1 }) original.append('todo/write', { todos: [{ content: 'only', status: 'completed' }] }) original.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('t4-replay'), original.snapshotEvents()) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayed.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos) .toEqual([{ content: 'only', status: 'completed' }]) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayed.snapshotEvents(SessionLogOffset(0), original.seq)).toEqual(original.snapshotEvents()) expect(replayed.firstLiveSeq).toBe(original.seq) }) diff --git a/packages/workflow/tool-workflow/src/invariant.ts b/packages/workflow/tool-workflow/src/invariant.ts index d36174b94f..1d41b5c216 100644 --- a/packages/workflow/tool-workflow/src/invariant.ts +++ b/packages/workflow/tool-workflow/src/invariant.ts @@ -135,6 +135,7 @@ const install: InvariantInstaller = Object.assign((ctx: Context, fail: Invariant const seed = (session: Session): WorkflowTrace => { const trace: WorkflowTrace = new Map() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents().filter(isWorkflowRecordEvent)) applyEvent(trace, event, fail) traces.set(session, trace) return trace diff --git a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts index e3404d6c12..e752ae307c 100644 --- a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts +++ b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts @@ -146,6 +146,7 @@ describe('dsh-tool-workflow', () => { engine.settleRun(runId, { value: 1, stopReason: 'completed', agentsStarted: 1 }) expect((await pending).isError).toBe(false) expect(engine.disposed).toBe(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => [event.type, event.data])).toEqual([ ['tool-workflow/run-start', { runId: 'run-1', name: 'audit' }], ['tool-workflow/agent-start', { @@ -166,9 +167,11 @@ describe('dsh-tool-workflow', () => { value: null, stopReason: 'completed', agentsStarted: 0, }) await vi.waitFor(() => { expect(engine.disposed).toBe(1) }) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual(['tool-workflow/run-start']) barrier.resolve(undefined) expect((await pending).isError).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual([ 'tool-workflow/run-start', 'tool-workflow/run-end', ]) @@ -190,8 +193,10 @@ describe('dsh-tool-workflow', () => { engine.settleRun(secondId, { value: null, stopReason: 'error', error: 'child failed', agentsStarted: 1 }) expect((await first).isError).toBe(false) expect((await second).isError).toBe(true) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'tool-workflow/agent-start')) .toHaveLength(1) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'tool-workflow/run-end').map(event => event.data)) .toEqual([ { runId: 'run-1', stopReason: 'completed' }, @@ -208,6 +213,7 @@ describe('dsh-tool-workflow', () => { await vi.waitFor(() => { expect(engine.requests).toHaveLength(1) }) engine.settleRun(WorkflowRunId('run-1'), { value: null, stopReason: 'completed', agentsStarted: 0 }) expect((await pending).isError).toBe(false) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -240,6 +246,7 @@ describe('dsh-tool-workflow', () => { expect(engine.disposed).toBe(1) expect(warnings).toHaveLength(1) expect(warnings[0]).toContain(failedType) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const types = session.snapshotEvents().map(event => event.type) const expectedPrefixes = { 'tool-workflow/run-start': [], diff --git a/scripts/oxlint-contract.spec.ts b/scripts/oxlint-contract.spec.ts index 1141067ad2..f358f71d9c 100644 --- a/scripts/oxlint-contract.spec.ts +++ b/scripts/oxlint-contract.spec.ts @@ -254,6 +254,59 @@ export const longProbe = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + } }, 90_000) + it('allows waived Session history reads while rejecting new reads and unrelated deprecations', async () => { + const suffix = randomUUID() + const configPath = await writeContractConfig(suffix) + const path = join(repositoryRoot, 'scripts', `oxlint-contract-${suffix}.ts`) + const existing = `import { Session, SessionSeq } from '@deepseek-ai/dsh-session' + +export function existingReads(session: Session): void { + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. + session.snapshotEvents() + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. + session.eventAt(SessionSeq(0)) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. + session.ownEvents() +} +` + const added = ` +/** @deprecated Use the replacement API. */ +function oldApi(): void {} + +export function newReads(session: Session): void { + session.snapshotEvents() + session.eventAt(SessionSeq(0)) + session.ownEvents() + oldApi() +} +` + + try { + await writeFile(path, existing) + const args = ['--config', relative(repositoryRoot, configPath), '--format', 'unix', relative(repositoryRoot, path)] + const allowed = runRepositoryOxlint(args) + expect(allowed.error).toBeUndefined() + expect(allowed.signal).toBeNull() + expect(allowed.status, normalizedOutput(allowed)).toBe(0) + + await writeFile(path, existing + added) + const rejected = runRepositoryOxlint(args) + const output = normalizedOutput(rejected) + expect(rejected.error).toBeUndefined() + expect(rejected.signal).toBeNull() + expect(rejected.status, output).toBe(1) + expect(output.match(/typescript\(no-deprecated\)/g), output).toHaveLength(4) + for (const method of ['snapshotEvents', 'eventAt', 'ownEvents', 'oldApi']) { + expect(output).toContain(`\`${method}\` is deprecated`) + } + } finally { + await Promise.all([ + rm(path, { force: true }), + rm(configPath, { force: true }), + ]) + } + }, 90_000) + it('accepts an ignored-only staged selection', () => { const result = runOxlint([ '--fix', diff --git a/scripts/python-snapshot-workflow-order.spec.ts b/scripts/python-snapshot-workflow-order.spec.ts index 964244f5da..84f401c51b 100644 --- a/scripts/python-snapshot-workflow-order.spec.ts +++ b/scripts/python-snapshot-workflow-order.spec.ts @@ -82,6 +82,7 @@ describe('advanced Python snapshot workflow ordering', () => { const child = await entered.promise expect(child.id).toBe(run.id) expect(adapter.requests).toHaveLength(0) + // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().some(event => event.type === 'subagent/descriptor')).toBe(false) parent.session.append('tool-workflow/agent-start', { runId: WorkflowRunId('run'), seq: 1, label: 'workflow-child', childId: child.id, From 8f986c8da63a212bfa6c64e4d58bbcafbc03a63e Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 12:55:32 +0800 Subject: [PATCH 04/20] chore(session): scope history reader exemptions to tests --- ...-synchronous-session-event-reads.i18n.yaml | 4 +- ...precate-synchronous-session-event-reads.md | 8 ++- ...cate-synchronous-session-event-reads.zh.md | 8 ++- .oxlintrc.json | 13 ++++ .../headless/tests/coding-task.e2e.ts | 1 - .../profiles/headless/tests/compaction.e2e.ts | 1 - .../profiles/headless/tests/full-loop.e2e.ts | 1 - .../tests/profiles/headless/tests/ptc.e2e.ts | 3 - .../profiles/headless/tests/resume.e2e.ts | 1 - .../profiles/headless/tests/todo-write.e2e.ts | 1 - .../tests/chat-continuous-conversation.e2e.ts | 1 - apps/web/tests/chat-long-interactions.e2e.ts | 11 --- apps/web/tests/chat-scroll-fixture.ts | 1 - apps/web/tests/clickable-links-gallery.e2e.ts | 1 - apps/web/tests/complex-history.perf.ts | 1 - apps/web/tests/feedback-release.e2e.ts | 1 - .../tests/goal-command-presentation.e2e.ts | 1 - apps/web/tests/markdown-cjk-strong.e2e.ts | 1 - apps/web/tests/markdown-images.e2e.ts | 1 - .../tests/markdown-inline-code-links.e2e.ts | 1 - apps/web/tests/markdown-wide-table.e2e.ts | 1 - apps/web/tests/math-rendering.e2e.ts | 1 - apps/web/tests/minimal-preset.snapshot.ts | 1 - apps/web/tests/preset-migration.snapshot.ts | 1 - apps/web/tests/produced-file-mentions.e2e.ts | 1 - apps/web/tests/produced-files.e2e.ts | 1 - apps/web/tests/reference-composer.e2e.ts | 2 - apps/web/tests/scaffold.ts | 2 - apps/web/tests/schedule-after.e2e.ts | 8 --- apps/web/tests/seeded-history.e2e.ts | 1 - apps/web/tests/settings-chrome.e2e.ts | 4 -- apps/web/tests/subagent-interrupt-ui.e2e.ts | 1 - apps/web/tests/subagent-interrupt.e2e.ts | 2 - packages/acp/acp/tests/bridge.spec.ts | 2 - packages/acp/acp/tests/turns.spec.ts | 5 -- .../commands-queue-attachment.host.spec.ts | 1 - .../tests/session-cold.host.spec.ts | 3 - .../tests/session-fork.host.spec.ts | 6 -- .../tests/session-projections.host.spec.ts | 3 - .../tests/session-rename.host.spec.ts | 1 - .../tests/transport.host.spec.ts | 1 - .../spill-policy-terminal.client.spec.ts | 1 - .../tests/command-compact.spec.ts | 1 - .../tests/loader-composition.spec.ts | 1 - .../tests/compaction-basic.spec.ts | 29 -------- .../tests/compaction-loop-repro.spec.ts | 12 ---- .../tests/manual-compaction.spec.ts | 29 -------- .../tests/tool-result-pruner.spec.ts | 4 -- .../compaction/tests/compaction.spec.ts | 3 - .../compaction/tests/invariant.spec.ts | 8 --- .../compaction/tests/tool-pairing.spec.ts | 1 - .../tests/agent-instructions.e2e.ts | 3 - .../tests/agent-instructions.spec.ts | 34 --------- .../tests/loader-composition.spec.ts | 1 - .../tests/session-reference.spec.ts | 4 -- .../time-context/tests/time-context.spec.ts | 13 ---- .../tmux-context/tests/tmux-context.spec.ts | 2 - .../agent-loop/tests/agent-initiator.spec.ts | 1 - packages/core/agent-loop/tests/agent.spec.ts | 4 -- packages/core/agent-loop/tests/cancel.spec.ts | 42 ----------- .../tests/contract-regressions.spec.ts | 40 ----------- .../agent-loop/tests/coverage-edges.spec.ts | 18 ----- packages/core/agent-loop/tests/inbox.spec.ts | 4 -- .../agent-loop/tests/interception.spec.ts | 1 - packages/core/agent-loop/tests/loop.spec.ts | 69 ------------------- .../core/agent-loop/tests/properties.spec.ts | 4 -- .../agent-loop/tests/request-cache.e2e.ts | 1 - .../agent-loop/tests/request-error.spec.ts | 6 -- .../agent-loop/tests/request-freeze.spec.ts | 3 - .../tests/request-reconstruction.spec.ts | 41 ----------- packages/core/agent-loop/tests/resume.spec.ts | 9 --- .../agent-loop/tests/scope-lifecycle.spec.ts | 3 - .../tests/system-prompt-admission.spec.ts | 20 ------ .../core/agent-loop/tests/tool-calls.spec.ts | 1 - .../core/agent-loop/tests/tool-order.spec.ts | 8 --- .../core/agent/tests/consumed-work.spec.ts | 13 ---- packages/core/session/README.i18n.yaml | 4 +- packages/core/session/README.md | 2 +- packages/core/session/README.zh.md | 2 +- .../session/tests/canonical-envelopes.spec.ts | 5 -- .../core/session/tests/derived-cache.spec.ts | 1 - packages/core/session/tests/fork.spec.ts | 13 ---- packages/core/session/tests/invariant.spec.ts | 2 - .../core/session/tests/properties.spec.ts | 8 --- .../core/session/tests/request-header.spec.ts | 1 - .../core/session/tests/sequence-types.spec.ts | 4 -- packages/core/session/tests/session.spec.ts | 53 -------------- packages/core/session/tests/surface.spec.ts | 20 ------ packages/core/tools/tests/invariant.spec.ts | 1 - packages/core/tools/tests/ptc.spec.ts | 1 - .../agent-team/tests/invariant.spec.ts | 2 - .../agent-team/tests/persistence.spec.ts | 3 - .../agent-team/tests/team.spec.ts | 7 -- .../agent-team/tests/test-session-query.ts | 1 - .../tool-agent-team/tests/tool-team.spec.ts | 1 - .../tests/command-feedback.spec.ts | 10 --- .../tests/loader-composition.spec.ts | 4 -- .../message-feedback/tests/helpers.ts | 1 - .../tests/message-feedback.spec.ts | 9 --- packages/fs/tool-fs/tests/fs-tools.e2e.ts | 1 - .../fs/tool-present/tests/present.spec.ts | 5 -- .../command-goal/tests/command-goal.spec.ts | 4 -- .../tests/goal-round-driver.spec.ts | 10 --- packages/goal/goal/tests/goal.spec.ts | 28 -------- packages/goal/goal/tests/projection.spec.ts | 1 - .../goal/tool-goal/tests/tool-goal.spec.ts | 2 - .../tests/repeat-tool-reminder.spec.ts | 3 - .../hooks/hook-protocol/tests/events.spec.ts | 9 --- .../hooks-claude-code/tests/bridge.spec.ts | 1 - .../hooks-claude-code/tests/coverage-cases.ts | 1 - .../hooks/hooks-codex/tests/bridge.spec.ts | 1 - .../hooks/hooks-codex/tests/coverage-cases.ts | 1 - .../commands/tests/commands.spec.ts | 5 -- .../tests/permission-presets.spec.ts | 18 ----- .../tests/projection.spec.ts | 4 -- .../user-approval/tests/approval.spec.ts | 9 --- .../tests/loader-composition.spec.ts | 1 - packages/llm/llm-retry/tests/retry.spec.ts | 25 ------- .../tests/transport-recovery.spec.ts | 17 ----- .../context-breakdown-projection.spec.ts | 6 -- .../llm/token-meter/tests/token-meter.spec.ts | 6 -- .../plan/plan-mode/tests/integration.spec.ts | 6 -- .../plan/plan-mode/tests/plan-mode.spec.ts | 39 ----------- .../plan/plan-mode/tests/projection.spec.ts | 1 - .../preset/agent-presets/tests/remote.spec.ts | 1 - .../sandbox-policy/tests/policy.spec.ts | 2 - .../schedule/schedule/tests/invariant.spec.ts | 5 -- .../schedule/tests/jsonl-restart.spec.ts | 2 - .../schedule/schedule/tests/runtime.spec.ts | 11 --- .../schedule/schedule/tests/tools.spec.ts | 9 --- .../tests/loader-composition.host.spec.ts | 1 - .../session-query/tests/session-query.spec.ts | 2 - .../tests/tool-session-query.spec.ts | 1 - .../tests/feedback-composition.spec.ts | 2 - .../session-log-deepseek/tests/upload.spec.ts | 4 -- .../tests/multi-edge-publication.spec.ts | 1 - .../tests/v2-system-migration.spec.ts | 6 -- .../session-telemetry-otel/tests/otel.spec.ts | 15 ---- .../session-telemetry/tests/redact.spec.ts | 2 - .../session-telemetry/tests/telemetry.spec.ts | 8 --- .../tests/provider.spec.ts | 1 - .../session-title-llm/tests/llm.spec.ts | 4 -- .../session-title/tests/projection.spec.ts | 1 - .../session-title/tests/provider.spec.ts | 3 - .../session-title/tests/rename.spec.ts | 3 - .../tests/service-contracts.spec.ts | 6 -- .../session-title/tests/session-title.spec.ts | 5 -- .../shell/tool-bash/tests/integration.spec.ts | 1 - packages/shell/tool-bash/tests/tools.spec.ts | 1 - .../skill/tool-skill/tests/tool-skill.spec.ts | 1 - .../tests/multi-subagent.spec.ts | 4 -- .../tests/subagent-fork-in-process.spec.ts | 8 --- .../tests/inheritance.spec.ts | 13 ---- .../tests/preset-inheritance.spec.ts | 1 - .../tests/structured.spec.ts | 6 -- .../tests/subagent-in-process-driver.spec.ts | 8 --- .../tests/spawn-in-process.e2e.ts | 1 - .../tests/subagent-spawn-in-process.spec.ts | 3 - .../subagent/tests/continuation.spec.ts | 12 ---- .../tests/model-selection-settings.spec.ts | 2 - .../terminal-bash/tests/index.spec.ts | 2 - .../tests/agent-loop-testkit.spec.ts | 1 - .../fixtures/workspace-context-compaction.ts | 2 - .../todo/tool-todo/tests/integration.spec.ts | 3 - .../todo/tool-todo/tests/invariant.spec.ts | 2 - .../tests/loader-composition.spec.ts | 2 - .../todo/tool-todo/tests/tool-todo.spec.ts | 10 --- .../tool-workflow/tests/tool-workflow.spec.ts | 7 -- scripts/oxlint-contract.spec.ts | 54 +++++++++++---- .../python-snapshot-workflow-order.spec.ts | 1 - 170 files changed, 72 insertions(+), 1070 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml index 51e978ca70..7a61bc540e 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml +++ b/.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: 0fcbfbc2e21792f67d1bdbe6d32ac3106338ac45 -2026-09-09-deprecate-synchronous-session-event-reads.zh.md: d1f22b483380a7a38a5cd1208815c5fc0ae25348 +2026-09-09-deprecate-synchronous-session-event-reads.md: 1420227daf9a410e7db6a7a685bcf4433d7d7b0b +2026-09-09-deprecate-synchronous-session-event-reads.zh.md: e546337bcb15cf6682935729de37aab0c579d2a2 diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md index 0fcbfbc2e2..1420227daf 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md +++ b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md @@ -16,6 +16,8 @@ All operations that synchronously read arbitrary positions or ranges of Session The three methods carry this rule in `@deprecated` JSDoc. This is an API-use decision; the current Session implementation still retains the complete event sequence in memory. +Repository test files may call these three readers to inspect emitted events and exercise Session history behavior. Their existing lint override allows `snapshotEvents`, `eventAt`, and `ownEvents`; all other deprecated names remain errors. This allowance also covers unrelated declarations with the same three names under the current linter. It does not apply to production source or ordinary repository scripts. + ### State needed after resume Design durable event fields and Session projections together so each domain can reconstruct the state its consumers need. Restore that state during resume, then maintain it incrementally from newly committed events. After resume, ordinary logic reads the projection or processes the delivered current event instead of looking back through historical events. Reading already-maintained projection state synchronously does not require arbitrary access to the event log. @@ -34,13 +36,15 @@ 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. +**Disable deprecation lint throughout test files or exempt production readers by name.** Tests only need the three reader names; other deprecated APIs must remain errors. Production code retains the prohibition on new synchronous reads. + +**Add a separate test-only reader API.** The three existing readers already expose the observations these tests need. Wrapping them adds an API and production-import checks without changing those observations. ## 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. -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. +Existing calls outside test files carry line-scoped `typescript/no-deprecated` waivers with a migration-deferral reason. Remove a waiver when its call migrates; copying a waiver to a new production call violates this policy. The executable lint check accepts test reads and existing waived reads, rejects unwaived production reads, and rejects unrelated deprecated APIs in tests. Documentation checks verify the source-equivalent API declarations and bilingual records. ## Related decisions diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md index d1f22b4833..e546337bcb 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md +++ b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md @@ -16,6 +16,8 @@ Status: implemented 这三个方法通过 `@deprecated` JSDoc 声明该规则。这是 API 使用决策;当前 Session 实现仍在内存中保留完整事件序列。 +仓库测试文件可以调用这三个读取方法,以检查已发出的事件并验证 Session 历史行为。现有测试 lint override 允许 `snapshotEvents`、`eventAt` 和 `ownEvents`,其他弃用名称仍报错。在当前 linter 下,此豁免也覆盖使用这三个名称的其他声明。它不适用于生产源码或普通仓库脚本。 + ### 恢复后需要的状态 结合设计持久事件字段与会话投影,使每个领域都能重建其消费方所需的状态。在恢复期间还原这些状态,随后通过新提交的事件增量维护。恢复后,普通逻辑读取投影或处理当前收到的事件,而不是回头查找历史事件。同步读取已经维护好的投影状态,不需要任意访问事件日志。 @@ -34,13 +36,15 @@ fork 等少数操作可能确实需要完整历史序列或继承前缀。它们 **要求立即迁移所有现有调用方。** 本决策允许现有逻辑推迟迁移。禁止新增依赖可以限制剩余工作规模,无需将每个现有消费方都纳入同一次修改。 -**为这些方法或整个文件禁用弃用 lint。** 这种豁免也会放过新增调用。逐行豁免明确标出已有的迁移负担,同时继续由原生类型感知规则检查新增读取。 +**在整个测试文件中禁用弃用 lint,或按名称豁免生产代码中的读取方法。** 测试只需要这三个读取方法名;其他弃用 API 必须继续报错。生产代码仍禁止新增同步读取。 + +**添加单独的测试专用读取 API。** 三个现有读取方法已经提供这些测试所需的观察结果。包装它们会增加一个 API 及生产代码导入检查,却不会改变这些观察结果。 ## 后果 新增领域行为必须使其事件数据与投影状态足以支持恢复后的执行。用户请求的历史仍可渐进式加载,确实需要完整历史的操作仍需设计存储读取路径。本决策不表示恢复或 fork 已经能够避免加载完整日志。 -现有调用带有逐行的 `typescript/no-deprecated` 豁免,并说明暂缓迁移的理由。该规则对新增调用及其他已弃用 API 继续生效。调用迁移后应删除豁免;将豁免复制到新增调用违反本策略。实际执行 lint 的检查覆盖三个读取方法有无豁免的情况,以及另一个已弃用 API。文档检查校验与源码一致的 API 声明及双语记录。 +测试文件之外的现有调用带有逐行的 `typescript/no-deprecated` 豁免,并说明暂缓迁移的理由。调用迁移后应删除豁免;将豁免复制到新增生产调用违反本策略。实际执行 lint 的检查允许测试读取和已有豁免调用,拒绝未豁免的生产读取,并拒绝测试中其他已弃用 API。文档检查校验与源码一致的 API 声明及双语记录。 ## 相关决策 diff --git a/.oxlintrc.json b/.oxlintrc.json index 1f11a9e989..348b057bc3 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -203,6 +203,19 @@ "scripts/**/*.spec.{ts,tsx}" ], "rules": { + // Session assertions inspect the log; other deprecated APIs remain errors. + "typescript/no-deprecated": [ + "error", + { + "allow": [ + { + "from": "file", + "name": ["snapshotEvents", "eventAt", "ownEvents"], + "path": "packages/core/session/src/index.ts" + } + ] + } + ], "typescript/no-invalid-void-type": "error", "typescript/no-non-null-assertion": "off", // Assertions commonly follow an expect() that proves presence. "typescript/no-unnecessary-condition": "off", diff --git a/apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts b/apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts index 4b78389bb3..bb0b4297b8 100644 --- a/apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/coding-task.e2e.ts @@ -67,7 +67,6 @@ 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) diff --git a/apps/cli/tests/profiles/headless/tests/compaction.e2e.ts b/apps/cli/tests/profiles/headless/tests/compaction.e2e.ts index 263b794778..6e3f46c266 100644 --- a/apps/cli/tests/profiles/headless/tests/compaction.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/compaction.e2e.ts @@ -56,7 +56,6 @@ 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. diff --git a/apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts b/apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts index 74db5cc44a..6816c60f61 100644 --- a/apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/full-loop.e2e.ts @@ -34,7 +34,6 @@ 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) diff --git a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts b/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts index 557b632132..c666871584 100644 --- a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts @@ -366,7 +366,6 @@ 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 @@ -419,12 +418,10 @@ 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' diff --git a/apps/cli/tests/profiles/headless/tests/resume.e2e.ts b/apps/cli/tests/profiles/headless/tests/resume.e2e.ts index ce8c0bd91d..579b082906 100644 --- a/apps/cli/tests/profiles/headless/tests/resume.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/resume.e2e.ts @@ -63,7 +63,6 @@ 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) }) diff --git a/apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts b/apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts index 7a201279f8..4ff5fef99c 100644 --- a/apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/todo-write.e2e.ts @@ -37,7 +37,6 @@ 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. diff --git a/apps/web/tests/chat-continuous-conversation.e2e.ts b/apps/web/tests/chat-continuous-conversation.e2e.ts index 062f5bceac..c539a96601 100644 --- a/apps/web/tests/chat-continuous-conversation.e2e.ts +++ b/apps/web/tests/chat-continuous-conversation.e2e.ts @@ -334,7 +334,6 @@ 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) diff --git a/apps/web/tests/chat-long-interactions.e2e.ts b/apps/web/tests/chat-long-interactions.e2e.ts index fbc0424c8c..17e639814c 100644 --- a/apps/web/tests/chat-long-interactions.e2e.ts +++ b/apps/web/tests/chat-long-interactions.e2e.ts @@ -184,17 +184,12 @@ 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 )) @@ -320,11 +315,8 @@ 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' }) @@ -342,13 +334,10 @@ 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' )) diff --git a/apps/web/tests/chat-scroll-fixture.ts b/apps/web/tests/chat-scroll-fixture.ts index bd060c57ff..34ebcc8d1b 100644 --- a/apps/web/tests/chat-scroll-fixture.ts +++ b/apps/web/tests/chat-scroll-fixture.ts @@ -178,7 +178,6 @@ 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') diff --git a/apps/web/tests/clickable-links-gallery.e2e.ts b/apps/web/tests/clickable-links-gallery.e2e.ts index 09423cd4ac..ea3b86dbb5 100644 --- a/apps/web/tests/clickable-links-gallery.e2e.ts +++ b/apps/web/tests/clickable-links-gallery.e2e.ts @@ -287,7 +287,6 @@ 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, diff --git a/apps/web/tests/complex-history.perf.ts b/apps/web/tests/complex-history.perf.ts index bc909b45f5..faa0146134 100644 --- a/apps/web/tests/complex-history.perf.ts +++ b/apps/web/tests/complex-history.perf.ts @@ -329,7 +329,6 @@ 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') diff --git a/apps/web/tests/feedback-release.e2e.ts b/apps/web/tests/feedback-release.e2e.ts index 30e12833ee..b63f3c6213 100644 --- a/apps/web/tests/feedback-release.e2e.ts +++ b/apps/web/tests/feedback-release.e2e.ts @@ -275,7 +275,6 @@ 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() diff --git a/apps/web/tests/goal-command-presentation.e2e.ts b/apps/web/tests/goal-command-presentation.e2e.ts index 157dbf947e..70ad37a799 100644 --- a/apps/web/tests/goal-command-presentation.e2e.ts +++ b/apps/web/tests/goal-command-presentation.e2e.ts @@ -124,7 +124,6 @@ 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']) diff --git a/apps/web/tests/markdown-cjk-strong.e2e.ts b/apps/web/tests/markdown-cjk-strong.e2e.ts index 40994debcb..237bbd9d3d 100644 --- a/apps/web/tests/markdown-cjk-strong.e2e.ts +++ b/apps/web/tests/markdown-cjk-strong.e2e.ts @@ -79,7 +79,6 @@ 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, diff --git a/apps/web/tests/markdown-images.e2e.ts b/apps/web/tests/markdown-images.e2e.ts index f7f369096b..b8f18c6c67 100644 --- a/apps/web/tests/markdown-images.e2e.ts +++ b/apps/web/tests/markdown-images.e2e.ts @@ -144,7 +144,6 @@ 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, diff --git a/apps/web/tests/markdown-inline-code-links.e2e.ts b/apps/web/tests/markdown-inline-code-links.e2e.ts index ac7be4e205..62bbf589aa 100644 --- a/apps/web/tests/markdown-inline-code-links.e2e.ts +++ b/apps/web/tests/markdown-inline-code-links.e2e.ts @@ -76,7 +76,6 @@ 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, diff --git a/apps/web/tests/markdown-wide-table.e2e.ts b/apps/web/tests/markdown-wide-table.e2e.ts index 9607969fef..09f980ab5c 100644 --- a/apps/web/tests/markdown-wide-table.e2e.ts +++ b/apps/web/tests/markdown-wide-table.e2e.ts @@ -142,7 +142,6 @@ 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, diff --git a/apps/web/tests/math-rendering.e2e.ts b/apps/web/tests/math-rendering.e2e.ts index 1363eb35cf..c826d5aff2 100644 --- a/apps/web/tests/math-rendering.e2e.ts +++ b/apps/web/tests/math-rendering.e2e.ts @@ -80,7 +80,6 @@ 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, diff --git a/apps/web/tests/minimal-preset.snapshot.ts b/apps/web/tests/minimal-preset.snapshot.ts index 900cb34055..22ab1bc476 100644 --- a/apps/web/tests/minimal-preset.snapshot.ts +++ b/apps/web/tests/minimal-preset.snapshot.ts @@ -81,7 +81,6 @@ 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) diff --git a/apps/web/tests/preset-migration.snapshot.ts b/apps/web/tests/preset-migration.snapshot.ts index a68c5c9f30..7fb3d881d2 100644 --- a/apps/web/tests/preset-migration.snapshot.ts +++ b/apps/web/tests/preset-migration.snapshot.ts @@ -45,7 +45,6 @@ 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'] : []) diff --git a/apps/web/tests/produced-file-mentions.e2e.ts b/apps/web/tests/produced-file-mentions.e2e.ts index 91919dad25..b8ec77c693 100644 --- a/apps/web/tests/produced-file-mentions.e2e.ts +++ b/apps/web/tests/produced-file-mentions.e2e.ts @@ -112,7 +112,6 @@ 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, diff --git a/apps/web/tests/produced-files.e2e.ts b/apps/web/tests/produced-files.e2e.ts index acb267b644..1097ed3b1e 100644 --- a/apps/web/tests/produced-files.e2e.ts +++ b/apps/web/tests/produced-files.e2e.ts @@ -99,7 +99,6 @@ 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, })), diff --git a/apps/web/tests/reference-composer.e2e.ts b/apps/web/tests/reference-composer.e2e.ts index 3870715531..39c7786296 100644 --- a/apps/web/tests/reference-composer.e2e.ts +++ b/apps/web/tests/reference-composer.e2e.ts @@ -73,7 +73,6 @@ 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') @@ -123,7 +122,6 @@ 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') diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index ada371450b..4bc734aa4b 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -877,7 +877,6 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise sessionFormatCatalog.encodeCurrentEvent(event)) const header = sessionFormatCatalog.encodeCurrentHeader({ @@ -1031,7 +1030,6 @@ 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('') diff --git a/apps/web/tests/schedule-after.e2e.ts b/apps/web/tests/schedule-after.e2e.ts index fd7a7bf461..587a355f4f 100644 --- a/apps/web/tests/schedule-after.e2e.ts +++ b/apps/web/tests/schedule-after.e2e.ts @@ -205,7 +205,6 @@ async function waitForReply( ): Promise> { 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 )) @@ -456,7 +455,6 @@ 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' @@ -472,7 +470,6 @@ 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' @@ -496,7 +493,6 @@ 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) @@ -520,7 +516,6 @@ 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' @@ -546,13 +541,11 @@ 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' @@ -567,7 +560,6 @@ 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' diff --git a/apps/web/tests/seeded-history.e2e.ts b/apps/web/tests/seeded-history.e2e.ts index 3472c80c57..09110c5360 100644 --- a/apps/web/tests/seeded-history.e2e.ts +++ b/apps/web/tests/seeded-history.e2e.ts @@ -495,7 +495,6 @@ 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') ?? [] diff --git a/apps/web/tests/settings-chrome.e2e.ts b/apps/web/tests/settings-chrome.e2e.ts index ed5b270188..8ef2af7048 100644 --- a/apps/web/tests/settings-chrome.e2e.ts +++ b/apps/web/tests/settings-chrome.e2e.ts @@ -144,7 +144,6 @@ 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' }) @@ -161,12 +160,10 @@ 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' }], @@ -184,7 +181,6 @@ 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' }], diff --git a/apps/web/tests/subagent-interrupt-ui.e2e.ts b/apps/web/tests/subagent-interrupt-ui.e2e.ts index 7fe4b6f2de..11d96c63fd 100644 --- a/apps/web/tests/subagent-interrupt-ui.e2e.ts +++ b/apps/web/tests/subagent-interrupt-ui.e2e.ts @@ -320,7 +320,6 @@ 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 }) diff --git a/apps/web/tests/subagent-interrupt.e2e.ts b/apps/web/tests/subagent-interrupt.e2e.ts index 9031d5577b..71e25c5918 100644 --- a/apps/web/tests/subagent-interrupt.e2e.ts +++ b/apps/web/tests/subagent-interrupt.e2e.ts @@ -159,9 +159,7 @@ 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') diff --git a/packages/acp/acp/tests/bridge.spec.ts b/packages/acp/acp/tests/bridge.spec.ts index 218a88ada4..ad1fa30fac 100644 --- a/packages/acp/acp/tests/bridge.spec.ts +++ b/packages/acp/acp/tests/bridge.spec.ts @@ -902,7 +902,6 @@ 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==') }) @@ -976,7 +975,6 @@ 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) }) diff --git a/packages/acp/acp/tests/turns.spec.ts b/packages/acp/acp/tests/turns.spec.ts index 8b8e38d8ab..a17223750f 100644 --- a/packages/acp/acp/tests/turns.spec.ts +++ b/packages/acp/acp/tests/turns.spec.ts @@ -216,7 +216,6 @@ 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) }) @@ -317,7 +316,6 @@ 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) }) @@ -445,7 +443,6 @@ 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' } }) }) @@ -471,14 +468,12 @@ 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' } }) }) diff --git a/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts b/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts index 22a38f92f9..d7559bc047 100644 --- a/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts +++ b/packages/api/session-controller/tests/commands-queue-attachment.host.spec.ts @@ -41,7 +41,6 @@ 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, { diff --git a/packages/api/session-controller/tests/session-cold.host.spec.ts b/packages/api/session-controller/tests/session-cold.host.spec.ts index 54003d6a65..eaca25062f 100644 --- a/packages/api/session-controller/tests/session-cold.host.spec.ts +++ b/packages/api/session-controller/tests/session-cold.host.spec.ts @@ -203,7 +203,6 @@ 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) @@ -718,7 +717,6 @@ 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'][] = [ [], @@ -743,7 +741,6 @@ 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({ diff --git a/packages/api/session-controller/tests/session-fork.host.spec.ts b/packages/api/session-controller/tests/session-fork.host.spec.ts index 355f99c0a6..1661f505bc 100644 --- a/packages/api/session-controller/tests/session-fork.host.spec.ts +++ b/packages/api/session-controller/tests/session-fork.host.spec.ts @@ -95,7 +95,6 @@ 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', ]) @@ -215,14 +214,12 @@ 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) } @@ -246,12 +243,10 @@ 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', @@ -263,7 +258,6 @@ 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({ diff --git a/packages/api/session-controller/tests/session-projections.host.spec.ts b/packages/api/session-controller/tests/session-projections.host.spec.ts index 9339df6779..90ffb10456 100644 --- a/packages/api/session-controller/tests/session-projections.host.spec.ts +++ b/packages/api/session-controller/tests/session-projections.host.spec.ts @@ -172,7 +172,6 @@ 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: { @@ -449,7 +448,6 @@ 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, }) }) @@ -484,7 +482,6 @@ 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) diff --git a/packages/api/session-controller/tests/session-rename.host.spec.ts b/packages/api/session-controller/tests/session-rename.host.spec.ts index 55ed1e32e0..60987608b9 100644 --- a/packages/api/session-controller/tests/session-rename.host.spec.ts +++ b/packages/api/session-controller/tests/session-rename.host.spec.ts @@ -76,7 +76,6 @@ 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' } }) diff --git a/packages/api/session-controller/tests/transport.host.spec.ts b/packages/api/session-controller/tests/transport.host.spec.ts index 9ef70e5d8d..ebc731769e 100644 --- a/packages/api/session-controller/tests/transport.host.spec.ts +++ b/packages/api/session-controller/tests/transport.host.spec.ts @@ -230,7 +230,6 @@ 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: {} }, diff --git a/packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts b/packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts index 0716dd609f..0db57e2139 100644 --- a/packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts +++ b/packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts @@ -70,7 +70,6 @@ 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]! diff --git a/packages/compaction/command-compact/tests/command-compact.spec.ts b/packages/compaction/command-compact/tests/command-compact.spec.ts index 92013469ac..f2ab167d69 100644 --- a/packages/compaction/command-compact/tests/command-compact.spec.ts +++ b/packages/compaction/command-compact/tests/command-compact.spec.ts @@ -121,7 +121,6 @@ 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) diff --git a/packages/compaction/command-compact/tests/loader-composition.spec.ts b/packages/compaction/command-compact/tests/loader-composition.spec.ts index 5b8402aae7..442d8196dc 100644 --- a/packages/compaction/command-compact/tests/loader-composition.spec.ts +++ b/packages/compaction/command-compact/tests/loader-composition.spec.ts @@ -130,7 +130,6 @@ 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', diff --git a/packages/compaction/compaction-basic/tests/compaction-basic.spec.ts b/packages/compaction/compaction-basic/tests/compaction-basic.spec.ts index df5cbce97c..bd6a48af66 100644 --- a/packages/compaction/compaction-basic/tests/compaction-basic.spec.ts +++ b/packages/compaction/compaction-basic/tests/compaction-basic.spec.ts @@ -625,7 +625,6 @@ 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) }) @@ -647,7 +646,6 @@ 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!) @@ -891,11 +889,9 @@ 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) }) @@ -922,7 +918,6 @@ 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, @@ -939,7 +934,6 @@ describe('compaction region transaction', () => { expect(head.content[0]?.type === 'text' ? head.content[0].text : '').toContain('') expect(head.content.at(-1)).toEqual({ type: 'text', text: '' }) - // 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()) }) @@ -964,7 +958,6 @@ 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' }) @@ -986,7 +979,6 @@ 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() }) @@ -1100,7 +1092,6 @@ 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' }) }) @@ -1115,7 +1106,6 @@ 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' }) }) @@ -1136,7 +1126,6 @@ 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) }) @@ -1156,7 +1145,6 @@ 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) }) @@ -1174,7 +1162,6 @@ 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) }) @@ -1448,7 +1435,6 @@ 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, @@ -1575,7 +1561,6 @@ describe('automatic listener and loader composition', () => { next: () => Promise = () => Promise.resolve(undefined), ): Promise { 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', @@ -1596,12 +1581,10 @@ 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) }) @@ -1619,7 +1602,6 @@ 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) }) @@ -1636,7 +1618,6 @@ 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) }) @@ -1696,7 +1677,6 @@ 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) }) @@ -1716,7 +1696,6 @@ 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) }) @@ -1735,7 +1714,6 @@ 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') @@ -1759,9 +1737,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().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')) @@ -1964,11 +1940,9 @@ 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) }) @@ -1981,7 +1955,6 @@ 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) }) @@ -2014,7 +1987,6 @@ 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) }) @@ -2156,7 +2128,6 @@ 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 diff --git a/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts b/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts index a5834df1c7..a527226c03 100644 --- a/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts +++ b/packages/compaction/compaction-basic/tests/compaction-loop-repro.spec.ts @@ -212,7 +212,6 @@ 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() } @@ -231,9 +230,7 @@ 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' } }, @@ -250,7 +247,6 @@ 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() @@ -283,7 +279,6 @@ 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( @@ -352,7 +347,6 @@ 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' } }, }) @@ -360,13 +354,11 @@ 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') @@ -436,7 +428,6 @@ 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, @@ -499,13 +490,10 @@ 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' } }, diff --git a/packages/compaction/compaction-basic/tests/manual-compaction.spec.ts b/packages/compaction/compaction-basic/tests/manual-compaction.spec.ts index daef651acd..9a118cd5a9 100644 --- a/packages/compaction/compaction-basic/tests/manual-compaction.spec.ts +++ b/packages/compaction/compaction-basic/tests/manual-compaction.spec.ts @@ -233,7 +233,6 @@ 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/')) } @@ -296,16 +295,13 @@ 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) @@ -340,7 +336,6 @@ 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) @@ -361,7 +356,6 @@ 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) }) @@ -412,18 +406,13 @@ 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 }) @@ -454,11 +443,8 @@ 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) @@ -476,7 +462,6 @@ 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) @@ -580,7 +565,6 @@ 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) }) @@ -600,7 +584,6 @@ 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) @@ -667,7 +650,6 @@ 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() @@ -704,9 +686,7 @@ 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 }) }) @@ -719,11 +699,8 @@ 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 }) }) @@ -740,7 +717,6 @@ 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() @@ -756,7 +732,6 @@ 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() } @@ -806,7 +781,6 @@ 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) }) @@ -851,7 +825,6 @@ 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) @@ -867,9 +840,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 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() diff --git a/packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts b/packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts index 7bcbd0ed62..a09d0a3c53 100644 --- a/packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts +++ b/packages/compaction/compaction-tool-result-pruner/tests/tool-result-pruner.spec.ts @@ -186,9 +186,7 @@ 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', @@ -223,7 +221,6 @@ 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: { @@ -259,7 +256,6 @@ 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) diff --git a/packages/compaction/compaction/tests/compaction.spec.ts b/packages/compaction/compaction/tests/compaction.spec.ts index 887362b18b..350d4b8308 100644 --- a/packages/compaction/compaction/tests/compaction.spec.ts +++ b/packages/compaction/compaction/tests/compaction.spec.ts @@ -131,7 +131,6 @@ 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); @@ -143,14 +142,12 @@ 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']) }) diff --git a/packages/compaction/compaction/tests/invariant.spec.ts b/packages/compaction/compaction/tests/invariant.spec.ts index 314ed70ba1..ff49a3400e 100644 --- a/packages/compaction/compaction/tests/invariant.spec.ts +++ b/packages/compaction/compaction/tests/invariant.spec.ts @@ -134,10 +134,8 @@ 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']) @@ -157,10 +155,8 @@ 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']) @@ -181,10 +177,8 @@ 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', @@ -210,10 +204,8 @@ 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) diff --git a/packages/compaction/compaction/tests/tool-pairing.spec.ts b/packages/compaction/compaction/tests/tool-pairing.spec.ts index a3dbb7e88c..a5694d1514 100644 --- a/packages/compaction/compaction/tests/tool-pairing.spec.ts +++ b/packages/compaction/compaction/tests/tool-pairing.spec.ts @@ -7,7 +7,6 @@ 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 } diff --git a/packages/context/agent-instructions/tests/agent-instructions.e2e.ts b/packages/context/agent-instructions/tests/agent-instructions.e2e.ts index 2c256150c8..7e35bdee21 100644 --- a/packages/context/agent-instructions/tests/agent-instructions.e2e.ts +++ b/packages/context/agent-instructions/tests/agent-instructions.e2e.ts @@ -77,7 +77,6 @@ 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) @@ -90,7 +89,6 @@ 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) @@ -104,7 +102,6 @@ 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' diff --git a/packages/context/agent-instructions/tests/agent-instructions.spec.ts b/packages/context/agent-instructions/tests/agent-instructions.spec.ts index 259a419bb4..d477cf666e 100644 --- a/packages/context/agent-instructions/tests/agent-instructions.spec.ts +++ b/packages/context/agent-instructions/tests/agent-instructions.spec.ts @@ -277,7 +277,6 @@ async function syncedWorkspaceContext(ctx: Context, agent: Agent): Promise event.type === 'user/message' && event.data.source.kind === 'agent-instructions' @@ -1128,7 +1127,6 @@ 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 { @@ -1148,16 +1146,13 @@ 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 { @@ -1180,12 +1175,10 @@ 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 { @@ -1208,15 +1201,12 @@ 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') @@ -1241,12 +1231,10 @@ 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) @@ -1280,7 +1268,6 @@ 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) @@ -1300,7 +1287,6 @@ 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) @@ -1335,7 +1321,6 @@ 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) @@ -1351,7 +1336,6 @@ 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) @@ -1387,7 +1371,6 @@ 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) @@ -1402,7 +1385,6 @@ 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) @@ -1433,7 +1415,6 @@ 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') @@ -1450,7 +1431,6 @@ 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) @@ -1481,7 +1461,6 @@ 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') @@ -1535,7 +1514,6 @@ 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') @@ -1577,7 +1555,6 @@ 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')) @@ -1612,7 +1589,6 @@ 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) @@ -1622,7 +1598,6 @@ 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 { @@ -1846,7 +1821,6 @@ 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. @@ -1855,7 +1829,6 @@ 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({ @@ -2037,7 +2010,6 @@ 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', ) @@ -2679,7 +2651,6 @@ 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) @@ -2687,7 +2658,6 @@ 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) @@ -3663,7 +3633,6 @@ 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({ @@ -3698,12 +3667,10 @@ 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') }], @@ -4734,7 +4701,6 @@ 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({ diff --git a/packages/context/session-reference/tests/loader-composition.spec.ts b/packages/context/session-reference/tests/loader-composition.spec.ts index 624b28de33..c680e393ca 100644 --- a/packages/context/session-reference/tests/loader-composition.spec.ts +++ b/packages/context/session-reference/tests/loader-composition.spec.ts @@ -134,7 +134,6 @@ 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) }) diff --git a/packages/context/session-reference/tests/session-reference.spec.ts b/packages/context/session-reference/tests/session-reference.spec.ts index cbb14be78d..dc2caa5067 100644 --- a/packages/context/session-reference/tests/session-reference.spec.ts +++ b/packages/context/session-reference/tests/session-reference.spec.ts @@ -322,7 +322,6 @@ 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 @@ -372,7 +371,6 @@ 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') @@ -453,7 +451,6 @@ 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() } }) @@ -1201,7 +1198,6 @@ 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) }) diff --git a/packages/context/time-context/tests/time-context.spec.ts b/packages/context/time-context/tests/time-context.spec.ts index 16e9a52a89..52700e6d62 100644 --- a/packages/context/time-context/tests/time-context.spec.ts +++ b/packages/context/time-context/tests/time-context.spec.ts @@ -68,7 +68,6 @@ 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' @@ -169,7 +168,6 @@ 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') @@ -293,9 +291,7 @@ 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({ @@ -308,17 +304,14 @@ 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) @@ -341,7 +334,6 @@ 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) @@ -349,7 +341,6 @@ 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) @@ -469,7 +460,6 @@ 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() }) @@ -492,10 +482,8 @@ 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) @@ -520,7 +508,6 @@ 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() diff --git a/packages/context/tmux-context/tests/tmux-context.spec.ts b/packages/context/tmux-context/tests/tmux-context.spec.ts index ef1b0e930c..bc9f9f0ed5 100644 --- a/packages/context/tmux-context/tests/tmux-context.spec.ts +++ b/packages/context/tmux-context/tests/tmux-context.spec.ts @@ -123,7 +123,6 @@ 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' @@ -172,7 +171,6 @@ 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 diff --git a/packages/core/agent-loop/tests/agent-initiator.spec.ts b/packages/core/agent-loop/tests/agent-initiator.spec.ts index f25740593a..0b9d32ec06 100644 --- a/packages/core/agent-loop/tests/agent-initiator.spec.ts +++ b/packages/core/agent-loop/tests/agent-initiator.spec.ts @@ -335,7 +335,6 @@ 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' })) diff --git a/packages/core/agent-loop/tests/agent.spec.ts b/packages/core/agent-loop/tests/agent.spec.ts index 1d5ea1ac41..2ec5bcf01c 100644 --- a/packages/core/agent-loop/tests/agent.spec.ts +++ b/packages/core/agent-loop/tests/agent.spec.ts @@ -35,7 +35,6 @@ 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) @@ -48,7 +47,6 @@ 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: '' }) @@ -99,7 +97,6 @@ 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) }) @@ -111,7 +108,6 @@ 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) }) diff --git a/packages/core/agent-loop/tests/cancel.spec.ts b/packages/core/agent-loop/tests/cancel.spec.ts index 00d9339fa6..2b1781083c 100644 --- a/packages/core/agent-loop/tests/cancel.spec.ts +++ b/packages/core/agent-loop/tests/cancel.spec.ts @@ -50,7 +50,6 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { /** 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,7 +71,6 @@ 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) }) @@ -88,14 +86,12 @@ 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' } }) @@ -150,7 +146,6 @@ 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' } }, @@ -201,7 +196,6 @@ 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) }) @@ -261,11 +255,8 @@ 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') @@ -295,7 +286,6 @@ 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) @@ -333,7 +323,6 @@ 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' }) @@ -411,7 +400,6 @@ 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) }) @@ -448,9 +436,7 @@ 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({ @@ -491,7 +477,6 @@ 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) }) @@ -508,7 +493,6 @@ 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' }]) @@ -519,7 +503,6 @@ 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')) @@ -554,14 +537,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 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), @@ -585,7 +566,6 @@ 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' }]) @@ -613,11 +593,9 @@ 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) }) @@ -640,9 +618,7 @@ 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') }) @@ -663,14 +639,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 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 => ( @@ -702,12 +676,10 @@ 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 } } }, }) @@ -728,7 +700,6 @@ 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) }) @@ -758,7 +729,6 @@ 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) }) @@ -794,9 +764,7 @@ 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) }) @@ -850,7 +818,6 @@ 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) }) @@ -882,7 +849,6 @@ 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) }) @@ -906,7 +872,6 @@ 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) }) @@ -928,11 +893,9 @@ 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) @@ -964,7 +927,6 @@ 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) @@ -976,7 +938,6 @@ 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) @@ -1004,7 +965,6 @@ 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', @@ -1026,7 +986,6 @@ 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' } }) }) @@ -1098,7 +1057,6 @@ 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' } }) diff --git a/packages/core/agent-loop/tests/contract-regressions.spec.ts b/packages/core/agent-loop/tests/contract-regressions.spec.ts index 841bbbd63f..68cb93f129 100644 --- a/packages/core/agent-loop/tests/contract-regressions.spec.ts +++ b/packages/core/agent-loop/tests/contract-regressions.spec.ts @@ -72,7 +72,6 @@ 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, @@ -112,7 +111,6 @@ 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') @@ -126,7 +124,6 @@ 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] @@ -177,7 +174,6 @@ 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' @@ -203,11 +199,9 @@ 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) @@ -249,23 +243,19 @@ 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' } }) }) @@ -320,7 +310,6 @@ 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] @@ -373,7 +362,6 @@ 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' } } }, }) @@ -410,9 +398,7 @@ 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) @@ -462,7 +448,6 @@ 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 @@ -525,7 +510,6 @@ 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' }]) @@ -559,7 +543,6 @@ 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' @@ -605,7 +588,6 @@ 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' }, }) @@ -674,7 +656,6 @@ 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 } } }) @@ -697,7 +678,6 @@ 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) }) @@ -728,7 +708,6 @@ 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({ @@ -765,7 +744,6 @@ 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, @@ -796,7 +774,6 @@ 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 }) @@ -830,7 +807,6 @@ 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) @@ -862,7 +838,6 @@ 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' } } }, }) @@ -956,7 +931,6 @@ 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 @@ -990,7 +964,6 @@ 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']) @@ -1020,14 +993,11 @@ 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. @@ -1060,7 +1030,6 @@ 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') @@ -1095,7 +1064,6 @@ 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) @@ -1126,7 +1094,6 @@ 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) @@ -1136,7 +1103,6 @@ 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) }) }) @@ -1169,7 +1135,6 @@ 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') { @@ -1234,7 +1199,6 @@ 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']) @@ -1283,7 +1247,6 @@ 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']) @@ -1334,7 +1297,6 @@ 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']) @@ -1382,7 +1344,6 @@ 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']) @@ -1428,7 +1389,6 @@ 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']) diff --git a/packages/core/agent-loop/tests/coverage-edges.spec.ts b/packages/core/agent-loop/tests/coverage-edges.spec.ts index f18e662e61..1fa24977b2 100644 --- a/packages/core/agent-loop/tests/coverage-edges.spec.ts +++ b/packages/core/agent-loop/tests/coverage-edges.spec.ts @@ -69,14 +69,12 @@ 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) }) @@ -103,7 +101,6 @@ 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) }) }) @@ -133,11 +130,8 @@ 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) @@ -161,7 +155,6 @@ 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 @@ -187,7 +180,6 @@ 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') { @@ -244,7 +236,6 @@ 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) @@ -271,7 +262,6 @@ 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) }) @@ -294,7 +284,6 @@ 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') }) @@ -324,7 +313,6 @@ 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') }) @@ -400,7 +388,6 @@ 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' }) }) @@ -449,7 +436,6 @@ 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') }) @@ -475,7 +461,6 @@ 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') { @@ -517,7 +502,6 @@ 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' }) }) @@ -540,10 +524,8 @@ 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') }) diff --git a/packages/core/agent-loop/tests/inbox.spec.ts b/packages/core/agent-loop/tests/inbox.spec.ts index 021e2339f7..028c42ca5c 100644 --- a/packages/core/agent-loop/tests/inbox.spec.ts +++ b/packages/core/agent-loop/tests/inbox.spec.ts @@ -142,7 +142,6 @@ 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]) @@ -247,7 +246,6 @@ 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() @@ -255,7 +253,6 @@ 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([ @@ -264,7 +261,6 @@ describe('ReactLoopInbox', () => { ]) agent.inbox.clear() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(beforeClear + 2) }) }) diff --git a/packages/core/agent-loop/tests/interception.spec.ts b/packages/core/agent-loop/tests/interception.spec.ts index 3decd9499e..b50fb45942 100644 --- a/packages/core/agent-loop/tests/interception.spec.ts +++ b/packages/core/agent-loop/tests/interception.spec.ts @@ -57,7 +57,6 @@ 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() } diff --git a/packages/core/agent-loop/tests/loop.spec.ts b/packages/core/agent-loop/tests/loop.spec.ts index 68b8683cb0..fb98a37e31 100644 --- a/packages/core/agent-loop/tests/loop.spec.ts +++ b/packages/core/agent-loop/tests/loop.spec.ts @@ -53,7 +53,6 @@ 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 : []) @@ -73,7 +72,6 @@ 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' } }) @@ -93,9 +91,7 @@ describe('agent loop', () => { (frame): frame is Extract => 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) @@ -137,7 +133,6 @@ 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) }) @@ -165,11 +160,9 @@ 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' } }, @@ -225,7 +218,6 @@ 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' } }, @@ -441,7 +433,6 @@ 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) }) @@ -465,14 +456,12 @@ 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') @@ -515,7 +504,6 @@ 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') @@ -578,7 +566,6 @@ 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' @@ -596,7 +583,6 @@ 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') @@ -644,7 +630,6 @@ 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' }, ]) @@ -659,7 +644,6 @@ 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' }) @@ -672,14 +656,12 @@ 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 { @@ -699,7 +681,6 @@ 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' @@ -741,9 +722,7 @@ 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']) }) @@ -756,7 +735,6 @@ 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' @@ -772,7 +750,6 @@ 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' @@ -793,7 +770,6 @@ 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' @@ -856,7 +832,6 @@ 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' @@ -878,7 +853,6 @@ 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 @@ -913,14 +887,12 @@ 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) @@ -939,14 +911,11 @@ 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([ @@ -975,9 +944,7 @@ 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) @@ -985,7 +952,6 @@ 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') }) @@ -998,9 +964,7 @@ 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: { @@ -1015,7 +979,6 @@ 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') @@ -1031,7 +994,6 @@ 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' }) @@ -1058,7 +1020,6 @@ 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' }] }, @@ -1071,12 +1032,9 @@ 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) @@ -1121,7 +1079,6 @@ 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) }) @@ -1167,7 +1124,6 @@ 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) }) @@ -1194,7 +1150,6 @@ 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') @@ -1224,7 +1179,6 @@ 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') }) @@ -1264,7 +1218,6 @@ 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() }) @@ -1297,16 +1250,13 @@ 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' }) }) @@ -1353,7 +1303,6 @@ 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' }) }) @@ -1458,7 +1407,6 @@ 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, @@ -1469,7 +1417,6 @@ 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, @@ -1510,7 +1457,6 @@ 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, @@ -1546,7 +1492,6 @@ 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, @@ -1590,7 +1535,6 @@ 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. @@ -1659,7 +1603,6 @@ 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') }) @@ -1681,9 +1624,7 @@ 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) @@ -1702,9 +1643,7 @@ 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) @@ -1760,11 +1699,9 @@ 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' }], @@ -1795,7 +1732,6 @@ 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' }) }) @@ -1849,7 +1785,6 @@ 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) }) @@ -1888,15 +1823,11 @@ 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') }) }) diff --git a/packages/core/agent-loop/tests/properties.spec.ts b/packages/core/agent-loop/tests/properties.spec.ts index bf3c723153..68e170f27e 100644 --- a/packages/core/agent-loop/tests/properties.spec.ts +++ b/packages/core/agent-loop/tests/properties.spec.ts @@ -72,21 +72,18 @@ 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) @@ -94,7 +91,6 @@ 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 diff --git a/packages/core/agent-loop/tests/request-cache.e2e.ts b/packages/core/agent-loop/tests/request-cache.e2e.ts index 5a1c912bc0..ceaa2b0b6d 100644 --- a/packages/core/agent-loop/tests/request-cache.e2e.ts +++ b/packages/core/agent-loop/tests/request-cache.e2e.ts @@ -82,7 +82,6 @@ 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) diff --git a/packages/core/agent-loop/tests/request-error.spec.ts b/packages/core/agent-loop/tests/request-error.spec.ts index f0ae238d80..616a817082 100644 --- a/packages/core/agent-loop/tests/request-error.spec.ts +++ b/packages/core/agent-loop/tests/request-error.spec.ts @@ -92,14 +92,12 @@ 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']) }) @@ -117,9 +115,7 @@ 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' } } }, @@ -141,9 +137,7 @@ 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' } }, diff --git a/packages/core/agent-loop/tests/request-freeze.spec.ts b/packages/core/agent-loop/tests/request-freeze.spec.ts index 82261178fe..560be88627 100644 --- a/packages/core/agent-loop/tests/request-freeze.spec.ts +++ b/packages/core/agent-loop/tests/request-freeze.spec.ts @@ -58,7 +58,6 @@ 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')! @@ -199,7 +198,6 @@ 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) @@ -223,7 +221,6 @@ 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' } } }, }) diff --git a/packages/core/agent-loop/tests/request-reconstruction.spec.ts b/packages/core/agent-loop/tests/request-reconstruction.spec.ts index e5ec05cb60..2450a21999 100644 --- a/packages/core/agent-loop/tests/request-reconstruction.spec.ts +++ b/packages/core/agent-loop/tests/request-reconstruction.spec.ts @@ -94,7 +94,6 @@ 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') @@ -112,7 +111,6 @@ 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']) }) @@ -134,7 +132,6 @@ 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']) }) @@ -160,7 +157,6 @@ 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([ @@ -198,7 +194,6 @@ 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']) }) @@ -228,7 +223,6 @@ 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'), @@ -248,7 +242,6 @@ 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 }, }) @@ -257,7 +250,6 @@ 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') @@ -276,7 +268,6 @@ 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) @@ -308,7 +299,6 @@ 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([ @@ -343,7 +333,6 @@ 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]) @@ -396,7 +385,6 @@ 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')) }) @@ -435,7 +423,6 @@ 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) }) @@ -459,7 +446,6 @@ 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 @@ -535,7 +521,6 @@ 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([ @@ -574,7 +559,6 @@ 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']) }) @@ -588,7 +572,6 @@ 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']) @@ -596,10 +579,8 @@ 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 }) @@ -629,10 +610,8 @@ 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') @@ -646,7 +625,6 @@ 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]!) }) @@ -670,11 +648,9 @@ 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']) @@ -684,7 +660,6 @@ 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') @@ -695,7 +670,6 @@ 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([]) @@ -729,12 +703,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 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']) }) @@ -753,10 +725,8 @@ 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 }) @@ -782,7 +752,6 @@ 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') @@ -810,7 +779,6 @@ 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() @@ -830,7 +798,6 @@ 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' }, }) @@ -838,13 +805,11 @@ 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]!) }) @@ -873,7 +838,6 @@ 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) @@ -900,7 +864,6 @@ 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) @@ -957,7 +920,6 @@ 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 }) @@ -983,7 +945,6 @@ 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]) @@ -996,7 +957,6 @@ 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' }]) @@ -1017,7 +977,6 @@ 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([ diff --git a/packages/core/agent-loop/tests/resume.spec.ts b/packages/core/agent-loop/tests/resume.spec.ts index 838c3e7004..271849eba3 100644 --- a/packages/core/agent-loop/tests/resume.spec.ts +++ b/packages/core/agent-loop/tests/resume.spec.ts @@ -484,7 +484,6 @@ 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() @@ -553,7 +552,6 @@ 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() @@ -641,7 +639,6 @@ 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')) @@ -694,7 +691,6 @@ 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() @@ -987,7 +983,6 @@ 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 @@ -1001,10 +996,8 @@ 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()) @@ -1012,10 +1005,8 @@ 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() diff --git a/packages/core/agent-loop/tests/scope-lifecycle.spec.ts b/packages/core/agent-loop/tests/scope-lifecycle.spec.ts index 851701a646..81bfc64abd 100644 --- a/packages/core/agent-loop/tests/scope-lifecycle.spec.ts +++ b/packages/core/agent-loop/tests/scope-lifecycle.spec.ts @@ -809,7 +809,6 @@ 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() @@ -1115,10 +1114,8 @@ 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() }) diff --git a/packages/core/agent-loop/tests/system-prompt-admission.spec.ts b/packages/core/agent-loop/tests/system-prompt-admission.spec.ts index e57df3e855..d3702147f6 100644 --- a/packages/core/agent-loop/tests/system-prompt-admission.spec.ts +++ b/packages/core/agent-loop/tests/system-prompt-admission.spec.ts @@ -39,7 +39,6 @@ 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() @@ -84,26 +83,21 @@ 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') @@ -130,7 +124,6 @@ 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) @@ -154,7 +147,6 @@ 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) @@ -174,7 +166,6 @@ 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) @@ -192,7 +183,6 @@ 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) @@ -201,7 +191,6 @@ 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') }) @@ -225,7 +214,6 @@ 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) @@ -242,12 +230,10 @@ 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' }, @@ -259,7 +245,6 @@ 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']) }) @@ -269,12 +254,10 @@ 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') }) @@ -283,7 +266,6 @@ describe('prepared-route prompt admission', () => { const entered = Promise.withResolvers() const release = Promise.withResolvers() 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') @@ -303,7 +285,6 @@ 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([]) @@ -322,7 +303,6 @@ 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) }) }) diff --git a/packages/core/agent-loop/tests/tool-calls.spec.ts b/packages/core/agent-loop/tests/tool-calls.spec.ts index dc6f783139..74871d4a0d 100644 --- a/packages/core/agent-loop/tests/tool-calls.spec.ts +++ b/packages/core/agent-loop/tests/tool-calls.spec.ts @@ -42,7 +42,6 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { } function events(agent: Agent): readonly SessionEvent[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() } diff --git a/packages/core/agent-loop/tests/tool-order.spec.ts b/packages/core/agent-loop/tests/tool-order.spec.ts index e4c05be289..287f5fe761 100644 --- a/packages/core/agent-loop/tests/tool-order.spec.ts +++ b/packages/core/agent-loop/tests/tool-order.spec.ts @@ -69,7 +69,6 @@ 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 @@ -83,7 +82,6 @@ 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)) @@ -91,7 +89,6 @@ 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']) @@ -106,15 +103,10 @@ 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) }) }) diff --git a/packages/core/agent/tests/consumed-work.spec.ts b/packages/core/agent/tests/consumed-work.spec.ts index 221a5f7d0f..26808c5c81 100644 --- a/packages/core/agent/tests/consumed-work.spec.ts +++ b/packages/core/agent/tests/consumed-work.spec.ts @@ -40,7 +40,6 @@ 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 }) }) @@ -49,7 +48,6 @@ 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' } }) }) @@ -63,7 +61,6 @@ 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) }) @@ -74,7 +71,6 @@ 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) }) @@ -89,7 +85,6 @@ 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) }) @@ -102,7 +97,6 @@ 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) }) @@ -115,7 +109,6 @@ 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) }) @@ -127,7 +120,6 @@ 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) }) @@ -138,9 +130,7 @@ 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, }) @@ -153,7 +143,6 @@ 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) }) @@ -163,9 +152,7 @@ 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, }) diff --git a/packages/core/session/README.i18n.yaml b/packages/core/session/README.i18n.yaml index 773d428da8..2e2efcb90f 100644 --- a/packages/core/session/README.i18n.yaml +++ b/packages/core/session/README.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 packages/core/session/README.md -README.md: ebe4c1c29992b29b07ad38d7368ca5cef2253ee0 -README.zh.md: f5d0ac59f1276e4fe59c1cc449dcf6ce5a082aba +README.md: e61481d1f5b62e24499b0db676a9ae62cce8f0c0 +README.zh.md: 9350a98c57261a4fc78487578717707a9393b502 diff --git a/packages/core/session/README.md b/packages/core/session/README.md index ebe4c1c299..e61481d1f5 100644 --- a/packages/core/session/README.md +++ b/packages/core/session/README.md @@ -55,7 +55,7 @@ Append, seed/restore, and event adoption/snapshot reject any `header.system` and ### Read the log -`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. `eventAt()`, `snapshotEvents()`, and `ownEvents()` are deprecated: existing logic may remain unmigrated for now, but new calls are prohibited ([rationale](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md)). Callers that only need a length use `seq`. +`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. `eventAt()`, `snapshotEvents()`, and `ownEvents()` are deprecated: existing logic may remain unmigrated for now, but new production calls are prohibited. Repository test files may use these three readers under their scoped lint allowance ([policy](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md)). Callers that only need a length use `seq`. Session log positions use two numeric types. `SessionSeq` identifies an existing event or inclusive event watermark; `SessionLogOffset` identifies a gap, prefix length, or read boundary and may equal the event count. `SessionSeqCursor` adds the `-1` “no event yet” value, while `OptionalSessionSeq` uses `null` when absence is data. The constructors validate non-negative safe integers, and the brands disappear at runtime, so durable JSON and wire values remain ordinary numbers. diff --git a/packages/core/session/README.zh.md b/packages/core/session/README.zh.md index f5d0ac59f1..9350a98c57 100644 --- a/packages/core/session/README.zh.md +++ b/packages/core/session/README.zh.md @@ -55,7 +55,7 @@ session.deriveMessages() // the derived model history ### 读取日志 -`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)` 会物化半开区间的冻结稳定快照;当前完整快照会缓存到下一次追加。`eventAt()`、`snapshotEvents()` 和 `ownEvents()` 已弃用:现有逻辑可以暂不迁移,但禁止新增调用([理由](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md))。只需要长度的调用方使用 `seq`。 +`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)` 会物化半开区间的冻结稳定快照;当前完整快照会缓存到下一次追加。`eventAt()`、`snapshotEvents()` 和 `ownEvents()` 已弃用:现有逻辑可以暂不迁移,但禁止新增生产调用。仓库测试文件可以在限定范围的 lint 豁免下使用这三个读取方法([策略](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md))。只需要长度的调用方使用 `seq`。 会话日志位置使用两种数字类型。`SessionSeq` 标识已有事件或包含端点的事件水位;`SessionLogOffset` 标识间隙、前缀长度或读取边界,并且可以等于事件数量。`SessionSeqCursor` 添加 `-1` 这个“尚无事件”值,`OptionalSessionSeq` 则在缺失本身属于数据时使用 `null`。构造函数会校验非负安全整数,品牌在运行时会被擦除,因此持久 JSON 与 wire 值仍是普通数字。 diff --git a/packages/core/session/tests/canonical-envelopes.spec.ts b/packages/core/session/tests/canonical-envelopes.spec.ts index 3177f333d6..123852762d 100644 --- a/packages/core/session/tests/canonical-envelopes.spec.ts +++ b/packages/core/session/tests/canonical-envelopes.spec.ts @@ -121,7 +121,6 @@ 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) } }) @@ -179,7 +178,6 @@ 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() @@ -205,7 +203,6 @@ 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) { @@ -213,7 +210,6 @@ 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) @@ -283,7 +279,6 @@ 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([]) diff --git a/packages/core/session/tests/derived-cache.spec.ts b/packages/core/session/tests/derived-cache.spec.ts index 35bf0fa15d..21d2b7153b 100644 --- a/packages/core/session/tests/derived-cache.spec.ts +++ b/packages/core/session/tests/derived-cache.spec.ts @@ -16,7 +16,6 @@ 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() } diff --git a/packages/core/session/tests/fork.spec.ts b/packages/core/session/tests/fork.spec.ts index a62019e23c..d67538ded8 100644 --- a/packages/core/session/tests/fork.spec.ts +++ b/packages/core/session/tests/fork.spec.ts @@ -47,7 +47,6 @@ 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 @@ -55,7 +54,6 @@ 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) } @@ -83,19 +81,13 @@ 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'), @@ -114,7 +106,6 @@ 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', @@ -132,7 +123,6 @@ 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([{ @@ -176,12 +166,9 @@ 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) diff --git a/packages/core/session/tests/invariant.spec.ts b/packages/core/session/tests/invariant.spec.ts index 778e4b560f..9b8b281cf5 100644 --- a/packages/core/session/tests/invariant.spec.ts +++ b/packages/core/session/tests/invariant.spec.ts @@ -83,7 +83,6 @@ 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 }) @@ -429,7 +428,6 @@ 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 })) diff --git a/packages/core/session/tests/properties.spec.ts b/packages/core/session/tests/properties.spec.ts index 200a1107f0..07bf37ac02 100644 --- a/packages/core/session/tests/properties.spec.ts +++ b/packages/core/session/tests/properties.spec.ts @@ -107,7 +107,6 @@ 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) })) @@ -116,11 +115,9 @@ 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) })) @@ -129,12 +126,9 @@ 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()) })) }) @@ -168,7 +162,6 @@ 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) @@ -177,7 +170,6 @@ 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) })) }) diff --git a/packages/core/session/tests/request-header.spec.ts b/packages/core/session/tests/request-header.spec.ts index 5aa4a5695b..c7b2719b6e 100644 --- a/packages/core/session/tests/request-header.spec.ts +++ b/packages/core/session/tests/request-header.spec.ts @@ -80,7 +80,6 @@ describe('foldRequestHeader', () => { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) session.append('request/header', { header: { config: { provider: 'mock', model: 'other' } }, reason: 'change' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldRequestHeader(session.snapshotEvents())).toEqual({ config: { provider: 'mock', model: 'other' } }) }) }) diff --git a/packages/core/session/tests/sequence-types.spec.ts b/packages/core/session/tests/sequence-types.spec.ts index b6aaee8a16..d20d2c721f 100644 --- a/packages/core/session/tests/sequence-types.spec.ts +++ b/packages/core/session/tests/sequence-types.spec.ts @@ -69,13 +69,11 @@ describe('Session log positions', () => { isSeeded: true, } - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = Session.create(id, source.snapshotEvents(), header, source.seq) expect(child.header.isSeeded).toBe(true) expect('seedLength' in child.header).toBe(false) expect(child.inheritedEventCount).toBe(2) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.ownEvents().map(event => event.type)).toEqual(['session/end-seed']) expect(child.isOwnSeq(SessionSeq(1))).toBe(false) expect(child.isOwnSeq(SessionSeq(2))).toBe(true) @@ -92,7 +90,6 @@ describe('Session log positions', () => { parent.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) const id = SessionId('suffix-child') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = Session.create(id, parent.snapshotEvents(), { version: SESSION_FORMAT_VERSION, id, @@ -101,7 +98,6 @@ describe('Session log positions', () => { }, parent.seq) child.append('request/context', { provider: 'provider', model: 'model' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.ownEvents().map(event => event.type)).toEqual([ 'session/end-seed', 'request/context', diff --git a/packages/core/session/tests/session.spec.ts b/packages/core/session/tests/session.spec.ts index ed5dfe46d4..d6762ba147 100644 --- a/packages/core/session/tests/session.spec.ts +++ b/packages/core/session/tests/session.spec.ts @@ -71,7 +71,6 @@ describe('Session', () => { session.append('turn/start', { turn: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'max-tokens' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEnd = session.snapshotEvents().findLast(e => e.type === 'turn/end')! expect(turnEnd.data.reason).toEqual({ kind: 'max-tokens' }) // survives a structuredClone (the persistence-serialization boundary) @@ -82,11 +81,8 @@ describe('Session', () => { const session = Session.create(SessionId('aborted')) session.append('turn/start', { turn: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'aborted', reason: { kind: 'user' } } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('aborted-replay'), structuredClone(session.snapshotEvents())) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayed.snapshotEvents().slice(0, -1)).toEqual(session.snapshotEvents()) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEnd = replayed.snapshotEvents().findLast(event => event.type === 'turn/end') expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason) .toEqual({ kind: 'aborted', reason: { kind: 'user' } }) @@ -119,7 +115,6 @@ describe('Session', () => { session.append('user/message', message, { surfaceOp: 'append' }) expect(session.deriveMessages()).toEqual([message]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents()[0] expect(event?.type === 'user/message' && event.data.source).toEqual({ kind: 'plugin', plugin: 'agent-instructions' }) }) @@ -144,11 +139,9 @@ describe('Session', () => { }, { surfaceOp: 'append' }) original.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('s3-replay'), original.snapshotEvents()) expect(replayed.deriveMessages()).toEqual(original.deriveMessages()) // The seed verbatim, plus the end-seed event the constructor appends. - // 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) expect(replayed.firstLiveSeq).toBe(original.seq) @@ -156,20 +149,16 @@ describe('Session', () => { it('marks an explicitly empty seed without marking a fresh session', () => { const fresh = Session.create(SessionId('fresh-empty')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(fresh.snapshotEvents()).toEqual([]) const resumed = Session.create(SessionId('resumed-empty'), []) expect(resumed.firstLiveSeq).toBe(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents()).toMatchObject([ { type: 'session/end-seed', seq: 0, data: {} }, ]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const reopened = Session.create(SessionId('reopened-empty'), resumed.snapshotEvents()) expect(reopened.firstLiveSeq).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(reopened.snapshotEvents()).toEqual(resumed.snapshotEvents()) }) @@ -199,7 +188,6 @@ describe('Session', () => { const unrelatedPrimitiveData = { type: 'plugin/event', seq: 0, time: 1, data: null, } as unknown as SessionEvent - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(Session.create(SessionId('primitive-plugin-data'), [unrelatedPrimitiveData]).snapshotEvents().slice(0, 1)) .toEqual([unrelatedPrimitiveData]) }) @@ -262,7 +250,6 @@ describe('Session', () => { SessionLogOffset(0), 'detached', ) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(restored.eventAt(SessionSeq(0))).toBe(mismatchedMessage) expect(Object.isFrozen(mismatchedMessage)).toBe(false) }) @@ -532,7 +519,6 @@ describe('Session', () => { reason: 'initial', }, } as const - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(Session.create(SessionId('reasoning-effort'), [valid]).snapshotEvents()[0]) .toEqual(valid) @@ -563,7 +549,6 @@ describe('Session', () => { reason: 'initial', }, } as const - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(Session.create(SessionId('adapter-defaults'), [valid]).snapshotEvents()[0]).toEqual(valid) for (const adapterDefaults of [ @@ -594,7 +579,6 @@ describe('Session', () => { isError: false, }), }, { surfaceOp: 'append' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = structuredClone(session.snapshotEvents()) // A misbehaving consumer tries to mutate the messages it was handed. @@ -611,7 +595,6 @@ describe('Session', () => { messages.reverse() // The log is unchanged: deep-equal to the snapshot taken before mutation. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual(before) // And a fresh derivation still reflects the original content and order. expect(session.deriveMessages()[0]!.content).toEqual([{ type: 'text', text: 'original' }]) @@ -641,7 +624,6 @@ describe('Session', () => { cyclic['self'] = cyclic expect(bad(cyclic)).toThrow(/non-JSON-serializable/) // The rejected appends never entered the log. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(0) }) @@ -656,14 +638,12 @@ describe('Session', () => { }))) .toThrow(/surface-eligible and requires a surfaceOp marker/) // The rejected append never entered the log (only turn/start is present). - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(1) }) it('accepts dense arrays and nested plain objects', () => { const session = Session.create(SessionId('s6')) expect(() => session.append('user/message', { content: [{ type: 'text', text: 'x' }], source: { kind: 'user' }, extra: [1, 2, [3, { a: null, b: true }]] } as never, { surfaceOp: 'append' })).not.toThrow() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(1) }) @@ -708,7 +688,6 @@ describe('Session', () => { { type: 'turn/end' as const, seq: 2, time: 3, data: { turn: 1, reason: { kind: 'completed' as const } } }, ] as SessionEvent[] const session = Session.create(SessionId('seed-ok'), goodSeed) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().slice(0, 3)).toEqual(goodSeed) expect(session.firstLiveSeq).toBe(3) }) @@ -734,7 +713,6 @@ describe('Session', () => { const session = Session.create(SessionId('seed-entry-snapshot'), seed) expect(reads).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().slice(0, 1)).toEqual([accepted]) }) @@ -752,7 +730,6 @@ describe('Session', () => { const session = Session.create(SessionId('seed-nested-drift'), seed) expect(reads).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()[0]!.data).toEqual({ value: 'accepted' }) }) @@ -814,7 +791,6 @@ describe('Session', () => { const session = Session.create(SessionId('seed-null-prototype'), [event]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().slice(0, 1)).toEqual([{ ...event }]) }) @@ -847,7 +823,6 @@ describe('Session', () => { }] as unknown as SessionEvent[] const session = Session.create(SessionId('seed-unstable-metadata'), seed) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents()[1]! if (event.type !== 'user/message') throw new Error('test fixture must remain a user/message') @@ -908,7 +883,6 @@ describe('Session', () => { const um = seed[1]! ;(um.data as { content: { type: 'text'; text: string }[] }).content[0]!.text = 'HACKED' ;(um.data as Record)['injected'] = 1n // would have failed validation - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[1]! expect(logged.type === 'user/message' && (logged.data.content[0] as { text: string }).text).toBe('original') expect((logged.data as Record)['injected']).toBeUndefined() @@ -927,7 +901,6 @@ describe('Session', () => { // make session.snapshotEvents() diverge from the value that passed validation. data.content[0]!.text = 'HACKED' ;(data as Record)['injected'] = 1n - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[0]! expect(logged.type === 'user/message' && (logged.data.content[0] as { text: string }).text).toBe('original') expect((logged.data as Record)['injected']).toBeUndefined() @@ -950,7 +923,6 @@ describe('Session', () => { expect(reads).toBe(1) expect(event.data).toEqual({ value: 'accepted' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([event]) }) @@ -964,7 +936,6 @@ describe('Session', () => { }), { surfaceOp: { op: 'replace', startSeq: 1n, endSeq: 2 } } as never, )).toThrow(/non-JSON-serializable surface metadata/) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -983,7 +954,6 @@ describe('Session', () => { }), { surfaceOp: new ReplaceOp() }, )).toThrow(/non-JSON-serializable surface metadata/) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -1015,7 +985,6 @@ describe('Session', () => { expect(reads).toBe(1) expect(event.surfaceOp).toEqual({ op: 'replace', startSeq: 0, endSeq: 0 }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([source, event]) }) @@ -1037,7 +1006,6 @@ describe('Session', () => { surfaceOp: 'append', sourceEventSeqs: [0, -1], })).toThrow(/non-negative safe integers/) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -1061,7 +1029,6 @@ describe('Session', () => { data: { turn: 1 }, surfaceOp: 'append', } as unknown as SessionEvent])).toThrow(/invalid seed event.*not surface-eligible/) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -1072,7 +1039,6 @@ describe('Session', () => { time: 1, data: { turn: 1 }, }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seededEvent = seeded.snapshotEvents()[0]! if (seededEvent.type !== 'turn/start') throw new Error('test fixture must remain a turn/start') expect(Object.isFrozen(seededEvent)).toBe(true) @@ -1093,24 +1059,20 @@ describe('Session', () => { it('returns cached frozen event-array snapshots that do not grow after append', () => { const session = Session.create(SessionId('events-snapshot')) session.append('turn/start', { turn: 1 }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents() const beforeEvent = before[0]! if (beforeEvent.type !== 'turn/start') throw new Error('test fixture must remain a turn/start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toBe(before) expect(Object.isFrozen(before)).toBe(true) expect(() => { (before as SessionEvent[]).push(beforeEvent) }).toThrow(TypeError) expect(() => { beforeEvent.data.turn = 99 }).toThrow(TypeError) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const after = session.snapshotEvents() expect(before).toHaveLength(1) expect(after).toHaveLength(2) expect(after).not.toBe(before) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toBe(after) }) @@ -1119,14 +1081,10 @@ describe('Session', () => { const start = session.append('turn/start', { turn: 1 }) const end = session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(0))).toBe(start) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(1))).toBe(end) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(2))).toBeUndefined() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const range = session.snapshotEvents(SessionLogOffset(1), SessionLogOffset(2)) expect(range).toEqual([end]) expect(Object.isFrozen(range)).toBe(true) @@ -1272,7 +1230,6 @@ describe('Session', () => { const marked = Session.create(SessionId('ignorable-envelope'), [ { ...base, ignorable: true } as SessionEvent, ]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(marked.snapshotEvents()[0]?.ignorable).toBe(true) }) }) @@ -1317,7 +1274,6 @@ describe('SessionStore', () => { a.append('user/message', createUserMessage({ content: [{ type: 'text', text: 'q' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const forked = ctx.sessions.create(SessionId('fork'), { seed: a.snapshotEvents() }) expect(forked.deriveMessages()).toEqual(a.deriveMessages()) }) @@ -1581,7 +1537,6 @@ describe('SessionStore', () => { const heard: SessionEvent[] = [] let committedBeforeNotify = false ctx.on('session/event', (observedSession, event) => { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. committedBeforeNotify = observedSession.snapshotEvents().at(-1) === event throw new Error('sync event observer') }) @@ -1595,7 +1550,6 @@ describe('SessionStore', () => { }) }).not.toThrow() expect(committedBeforeNotify).toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(heard).toEqual([appended]) await Promise.resolve() @@ -1619,7 +1573,6 @@ describe('SessionStore', () => { const [observedSession, event] = args as [Session, SessionEvent] validations.push({ event, - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. logLength: observedSession.snapshotEvents().length, frozen: Object.isFrozen(event) && Object.isFrozen(event.data), }) @@ -1633,7 +1586,6 @@ describe('SessionStore', () => { expect(() => session.append('turn/start', { turn: 1, })).toThrow('reject first candidate') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) expect(observed).toEqual([]) @@ -1646,7 +1598,6 @@ describe('SessionStore', () => { ]) expect(validations.map(({ event }) => event.seq)).toEqual([0, 0]) expect(validations[1]!.event).toBe(appended) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(observed).toEqual([appended]) }) @@ -1678,7 +1629,6 @@ describe('SessionStore', () => { sourceEventSeqs: [SessionSeq(2)], })).toThrow('reject surface candidate') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(3) expect(surface.nodes).toEqual([2]) expect(surface.replaceGeneration).toBe(0) @@ -1704,7 +1654,6 @@ describe('SessionStore', () => { expect(() => session.append('turn/start', { turn: 1, })).toThrow('dispatch instrumentation rejected the carrier') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) expect(observed).toEqual([]) }) @@ -1724,7 +1673,6 @@ describe('SessionStore', () => { const appended = session.append('turn/start', { turn: 1, }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(heard).toEqual([appended]) expect(warnings).toEqual([ @@ -1756,7 +1704,6 @@ describe('SessionStore', () => { turn: 1, }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([appended]) expect(order).toEqual(['resolve:live', 'observe:live', 'dispose:detached']) expect(ctx.sessions.get(session.id)).toBeUndefined() diff --git a/packages/core/session/tests/surface.spec.ts b/packages/core/session/tests/surface.spec.ts index 5436777e22..6d10e22335 100644 --- a/packages/core/session/tests/surface.spec.ts +++ b/packages/core/session/tests/surface.spec.ts @@ -328,7 +328,6 @@ describe('SurfaceManager', () => { sourceEventSeqs: sourceSeqs(2, 1), }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const folded = foldSurface(s.snapshotEvents()) expect(folded.nodes).toEqual(s.surface.nodes) expect(folded.replacements).toEqual([ @@ -338,9 +337,7 @@ describe('SurfaceManager', () => { folded.nodes[0] = SessionSeq(99) folded.replacements[0]!.shadowedSeqs.push(SessionSeq(99)) expect(s.surface.nodes).toEqual([3]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(s.snapshotEvents()).nodes).toEqual([3]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(s.snapshotEvents()).replacements[0]!.shadowedSeqs).toEqual([0]) }) @@ -357,7 +354,6 @@ describe('SurfaceManager', () => { expect(s.surface.nodes).toEqual([1]) const manager = s.surface as unknown as { _state: object } expect(Object.hasOwn(manager._state, 'replacements')).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(s.snapshotEvents()).replacements).toEqual([ { seq: 1, start: 0, end: 0, shadowedSeqs: [0] }, ]) @@ -392,7 +388,6 @@ describe('SurfaceManager', () => { const surface = s.surface const nodes = surface.nodes - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(nodes).toEqual(foldSurface(s.snapshotEvents()).nodes) expect(surface.replaceGeneration).toBe(0) @@ -413,12 +408,10 @@ describe('SurfaceManager', () => { { surfaceOp: surfaceOp({ op: 'replace', startSeq: 0, endSeq: 0 }) }, )).toThrow(/missing 0/) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(s.snapshotEvents()).toHaveLength(1) expect(s.surface).toBe(surface) expect(surface.nodes).toEqual([0]) expect(surface.replaceGeneration).toBe(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(surface.nodes).toEqual(foldSurface(s.snapshotEvents()).nodes) s.append('user/message', createUserMessage({ @@ -427,7 +420,6 @@ describe('SurfaceManager', () => { expect(surface.nodes).toBe(nodes) expect(surface.nodes).toEqual([0, 1]) expect(surface.replaceGeneration).toBe(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(surface.nodes).toEqual(foldSurface(s.snapshotEvents()).nodes) }) @@ -501,7 +493,6 @@ describe('SurfaceManager', () => { isError: false, }), }, { surfaceOp: 'append' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('replay'), original.snapshotEvents()) expect(replayed.surface.nodes).toEqual([1, 2, 4]) expect(replayed.deriveMessages()).toEqual(original.deriveMessages()) @@ -603,7 +594,6 @@ describe('SurfaceManager', () => { // Mutate caller's array after append. sources.push(SessionSeq(1)) sources[0] = SessionSeq(99) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = s.snapshotEvents()[1]! as SurfaceEvent expect(logged.sourceEventSeqs).toEqual([0]) }) @@ -639,7 +629,6 @@ describe('SurfaceManager', () => { }) // Mutate caller's object after append. op.startSeq = SessionSeq(99) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = s.snapshotEvents()[1]! as SurfaceEvent expect(logged.surfaceOp).toEqual({ op: 'replace', startSeq: 0, endSeq: 0 }) }) @@ -730,9 +719,7 @@ describe('Session.append surface opts', () => { expect(event.sourceEventSeqs).toEqual([0, 1]) expect(event.surfaceOp).toBe('append') // The logged event matches the returned event. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[2]! as SurfaceEvent).sourceEventSeqs).toEqual([0, 1]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[2]! as SurfaceEvent).surfaceOp).toBe('append') }) @@ -766,9 +753,7 @@ describe('Session.append surface opts', () => { it('a non-surface event carries no surface fields', () => { const s = Session.create(SessionId('noopts')) s.append('turn/start', { turn: 1 }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[0] as SessionEvent).sourceEventSeqs).toBeUndefined() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((s.snapshotEvents()[0] as SessionEvent).surfaceOp).toBeUndefined() }) @@ -821,14 +806,12 @@ describe('surface type guards', () => { it('isSurfaceEvent narrows a fully-formed surface event', () => { const s = surfaceSession() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const userMessage = s.snapshotEvents().find(e => e.type === 'user/message')! expect(isSurfaceEvent(userMessage)).toBe(true) }) it('isSurfaceEvent rejects a non-surface-eligible type', () => { const s = surfaceSession() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnStart = s.snapshotEvents().find(e => e.type === 'turn/start')! expect(isSurfaceEvent(turnStart)).toBe(false) }) @@ -852,9 +835,7 @@ describe('surface type guards', () => { s.append('user/message', createUserMessage({ content: [{ type: 'text', text: 'checkpoint' }], source: { kind: 'plugin', plugin: 'compact' }, }), { surfaceOp: surfaceOp({ op: 'replace', startSeq: 1, endSeq: 2 }), sourceEventSeqs: sourceSeqs(1, 2) }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const appended = s.snapshotEvents().find(e => e.type === 'user/message')! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replacement = s.snapshotEvents().at(-1)! expect(isAppendSurfaceEvent(appended)).toBe(true) @@ -865,7 +846,6 @@ describe('surface type guards', () => { it('rejects log-only and markerless events from both marker guards', () => { const s = surfaceSession() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnStart = s.snapshotEvents().find(e => e.type === 'turn/start')! // A surface-eligible type whose mandatory marker is absent has no origin at // all: it never entered the surface. diff --git a/packages/core/tools/tests/invariant.spec.ts b/packages/core/tools/tests/invariant.spec.ts index 795bef2ad8..202963379e 100644 --- a/packages/core/tools/tests/invariant.spec.ts +++ b/packages/core/tools/tests/invariant.spec.ts @@ -152,7 +152,6 @@ describe('tool-pipeline invariants', () => { name: 'echo', arguments: {}, })).toThrow(/parentCallId child does not belong to rootCallId another-root/) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().some(event => event.type === 'tool/ptc-dispatch-start' && String(event.data.subCallId) === 'invalid-grandchild')).toBe(false) }) diff --git a/packages/core/tools/tests/ptc.spec.ts b/packages/core/tools/tests/ptc.spec.ts index 2f2dfebd1a..11bf14e01a 100644 --- a/packages/core/tools/tests/ptc.spec.ts +++ b/packages/core/tools/tests/ptc.spec.ts @@ -1484,7 +1484,6 @@ describe('the run_code dispatch bridge', () => { expect(result.isError).toBe(false) expect(result.isError ? undefined : result.value).toEqual({ logs: [], result: depth }) expect({ observedDepth, observedLeaf }).toEqual({ observedDepth: depth, observedLeaf: 'leaf' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const dispatch = session.snapshotEvents().find(event => event.type === 'tool/ptc-dispatch') if (dispatch === undefined) throw new Error('expected a durable tool/ptc-dispatch event') const logged = dispatch.data.arguments as { nested: JsonValue } diff --git a/packages/experimental/agent-team/tests/invariant.spec.ts b/packages/experimental/agent-team/tests/invariant.spec.ts index df9ec3b3a7..5dd8dba256 100644 --- a/packages/experimental/agent-team/tests/invariant.spec.ts +++ b/packages/experimental/agent-team/tests/invariant.spec.ts @@ -44,7 +44,6 @@ describe('Agent Teams stream invariant', () => { code: 'INVARIANT', packageName: '@deepseek-ai/dsh-experimental-agent-team', })) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(invalid.snapshotEvents()).toEqual([]) }) @@ -70,7 +69,6 @@ describe('Agent Teams stream invariant', () => { code: 'INVARIANT', packageName: '@deepseek-ai/dsh-experimental-agent-team', })) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) }) diff --git a/packages/experimental/agent-team/tests/persistence.spec.ts b/packages/experimental/agent-team/tests/persistence.spec.ts index c7d4414792..5237fc0301 100644 --- a/packages/experimental/agent-team/tests/persistence.spec.ts +++ b/packages/experimental/agent-team/tests/persistence.spec.ts @@ -31,7 +31,6 @@ function durable(agent: Agent): { pendingMessages: TeamMessageSnapshot[] } { let projected = teamProjectionDefinition.init(agent.session.header) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of agent.session.snapshotEvents()) projected = teamProjectionDefinition.apply(projected, event) if (projected.failure !== undefined) throw new Error(projected.failure) const state = projected @@ -159,7 +158,6 @@ async function persistedChild( // Live sessions persist only through an attached agent-loop writer; this // bare fixture session seeds its durable log directly for the cold restart. const handle = await ctx.sessionPersistence.create(child.header) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await handle.append(child.snapshotEvents()) await handle.close() return child @@ -395,7 +393,6 @@ for (const backend of backends) { const checkpointEntered = Promise.withResolvers() const releaseCheckpoint = Promise.withResolvers() const delayedCheckpoint = vi.spyOn(second.ctx.sessions, 'flush').mockImplementation(async (session) => { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. if (session.id === rootId && session.snapshotEvents().some(event => event.type === 'team/message/delivered' && event.data.messageId === messageId)) { checkpointEntered.resolve(undefined) diff --git a/packages/experimental/agent-team/tests/team.spec.ts b/packages/experimental/agent-team/tests/team.spec.ts index d19f1522b7..79354a6427 100644 --- a/packages/experimental/agent-team/tests/team.spec.ts +++ b/packages/experimental/agent-team/tests/team.spec.ts @@ -35,7 +35,6 @@ function durable(agent: Agent): { pendingMessages: TeamMessageSnapshot[] } { let projected = teamProjectionDefinition.init(agent.session.header) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of agent.session.snapshotEvents()) projected = teamProjectionDefinition.apply(projected, event) if (projected.failure !== undefined) throw new Error(projected.failure) const state = projected @@ -271,7 +270,6 @@ describe('Team identity and provisioning', () => { // Live sessions persist only through an attached agent-loop writer; this // bare fixture session seeds its durable log directly for the cold reread. const persisted = await ctx.sessionPersistence.create(liveSession.header) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await persisted.append(liveSession.snapshotEvents()) await persisted.close() await liveFiber.dispose() @@ -471,7 +469,6 @@ describe('Team identity and provisioning', () => { await ctx.agentTeams.createTask(lead, { subject: 'parent task', description: 'belongs to parent' }) const handle = await ctx.agents.create({ sessionId: SessionId('ordinary-fork'), - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: lead.session.snapshotEvents(), meta: { parentSession: lead.id, isSeeded: true }, inheritedEventCount: SessionLogOffset(lead.session.seq), @@ -954,7 +951,6 @@ describe('Team mailbox and waiting', () => { }) await expect(teamInternals(ctx).mailbox.tryDispatch(lead, message, SIGNAL)).resolves.toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(lead.session.snapshotEvents().some(event => event.type === 'agent/inbox/spliced' && event.data.inserted.some(input => input.source.kind === 'team-message' && input.source.messageId === message.id))).toBe(true) @@ -999,14 +995,12 @@ describe('Team mailbox and waiting', () => { 'team/message/delivered', ]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const receiptCount = lead.session.snapshotEvents().filter(event => event.type === 'agent/inbox/spliced' && event.data.inserted.some(message => message.source.kind === 'team-message' && messageIds.has(message.source.messageId))).length await teamFiber.dispose() await ctx.plugin(TeamService, { maxPendingMessagesPerMember: 1 }) await vi.waitFor(() => { expect(durable(lead).pendingMessages).toEqual([]) }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(lead.session.snapshotEvents().filter(event => event.type === 'agent/inbox/spliced' && event.data.inserted.some(message => message.source.kind === 'team-message' && messageIds.has(message.source.messageId)))).toHaveLength(receiptCount) @@ -1208,7 +1202,6 @@ describe('Team mailbox and waiting', () => { expect(later.status).toBe('accepted') const target = await waitRunning(ctx, started.member.id) await vi.waitFor(() => { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const accepted = target.session.snapshotEvents().flatMap(event => event.type === 'agent/inbox/spliced' ? event.data.inserted.flatMap(message => message.source.kind === 'team-message' ? [message.source.messageId] diff --git a/packages/experimental/agent-team/tests/test-session-query.ts b/packages/experimental/agent-team/tests/test-session-query.ts index 3b7554873a..233c493ad2 100644 --- a/packages/experimental/agent-team/tests/test-session-query.ts +++ b/packages/experimental/agent-team/tests/test-session-query.ts @@ -33,7 +33,6 @@ export class TestSessionQuery extends SessionQueryEngine { options: SessionObservationOptions = {}, ): Promise { const live = this.ctx.sessions.get(sessionId) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. if (live !== undefined) return cut('live', live.header, live.snapshotEvents()) const handle = await this.ctx.sessionPersistence.open( sessionId, diff --git a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts index 74a8175fcd..6ae2fddca3 100644 --- a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts +++ b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts @@ -148,7 +148,6 @@ describe('dsh-tool-team', () => { expect(childAssembly.tools.map(schema => schema.name).filter(name => TOOL_NAMES.includes(name)).sort()) .toEqual(TOOL_NAMES) expect(renderPrompt(childAssembly)).toContain('Your Team role is teammate; your Team name is tool-worker') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const initialPrompt = child.session.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'user') expect(initialPrompt?.type === 'user/message' diff --git a/packages/feedback/command-feedback/tests/command-feedback.spec.ts b/packages/feedback/command-feedback/tests/command-feedback.spec.ts index 40ef1755b4..e59020bfc6 100644 --- a/packages/feedback/command-feedback/tests/command-feedback.spec.ts +++ b/packages/feedback/command-feedback/tests/command-feedback.spec.ts @@ -73,7 +73,6 @@ async function run(test: Harness, suffix = ''): Promise<{ kind: string; text?: s /** Authoritative feedback payloads in log order. */ function feedbackTexts(session: Session): string[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents() .filter(event => event.type === 'feedback/record') .map(event => event.data.text) @@ -108,17 +107,14 @@ describe('/feedback human command', () => { text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}.`, }) expect(feedbackTexts(test.session)).toEqual(['the diff view is unreadable']) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const commandRun = test.session.snapshotEvents().find(event => event.type === 'command/run') expect(commandRun?.type === 'command/run' && Object.hasOwn(commandRun.data, 'args')).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(JSON.stringify(test.session.snapshotEvents()).match(/the diff view is unreadable/gu)).toHaveLength(1) }) it('exports a command-independent feedback producer', async () => { const test = await harness() commandFeedback.recordFeedback(test.session, ' recorded outside a command ') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.session.snapshotEvents().map(event => event.type)).toEqual(['feedback/record']) expect(feedbackTexts(test.session)).toEqual(['recorded outside a command']) expect(() => { commandFeedback.recordFeedback(test.session, ' \n\t ') }) @@ -129,7 +125,6 @@ describe('/feedback human command', () => { it('keeps command bookkeeping around the authoritative feedback event', async () => { const test = await harness() await run(test, ' nothing else happens') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.session.snapshotEvents().map(event => event.type)).toEqual([ 'command/run', 'feedback/record', 'command/done', ]) @@ -166,12 +161,10 @@ describe('/feedback human command', () => { it('keeps every recorded event out of model context and derived history', async () => { const test = await harness() await run(test, ' invisible to the model') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of test.session.snapshotEvents()) { expect('surfaceOp' in event).toBe(false) expect(test.session.deriveEventMessage(event)).toBeNull() } - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSurface(test.session.snapshotEvents()).nodes).toEqual([]) expect(test.session.surface.nodes).toEqual([]) expect(test.session.deriveMessages()).toEqual([]) @@ -187,10 +180,8 @@ describe('/feedback human command', () => { await expect(run(test, ' \n\t ')).resolves.toEqual(expected) expect(getOrCreateAnonymousUserId).not.toHaveBeenCalled() expect(feedbackTexts(test.session)).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const done = test.session.snapshotEvents().filter(event => event.type === 'command/done') expect(done.map(event => event.data.kind)).toEqual(['error', 'error']) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of test.session.snapshotEvents()) { if (event.type === 'command/run') expect(Object.hasOwn(event.data, 'args')).toBe(false) } @@ -202,7 +193,6 @@ describe('/feedback human command', () => { controller.abort(new Error('user cancelled the command')) await expect(test.ctx.commands.execute(test.agent, '/feedback too late', [], controller.signal)) .rejects.toThrow('user cancelled the command') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.session.snapshotEvents()).toEqual([]) }) }) diff --git a/packages/feedback/command-feedback/tests/loader-composition.spec.ts b/packages/feedback/command-feedback/tests/loader-composition.spec.ts index 6867625302..6ab826e516 100644 --- a/packages/feedback/command-feedback/tests/loader-composition.spec.ts +++ b/packages/feedback/command-feedback/tests/loader-composition.spec.ts @@ -102,16 +102,12 @@ describe('/feedback real Loader composition through cordis.yml', () => { }) // The domain event owns the payload; generic command bookkeeping omits it. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().map(event => event.type)) .toEqual(['command/run', 'feedback/record', 'command/done', 'command/run', 'command/done']) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const run = owner.session.snapshotEvents().find(event => event.type === 'command/run') expect(run?.type === 'command/run' && Object.hasOwn(run.data, 'args')).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const feedback = owner.session.snapshotEvents().find(event => event.type === 'feedback/record') expect(feedback?.type === 'feedback/record' && feedback.data.text).toBe('the diff view is unreadable') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(JSON.stringify(owner.session.snapshotEvents()).match(/the diff view is unreadable/gu)).toHaveLength(1) // Nothing reached the model. diff --git a/packages/feedback/message-feedback/tests/helpers.ts b/packages/feedback/message-feedback/tests/helpers.ts index 8f3f6c3bb8..b2ac2f1943 100644 --- a/packages/feedback/message-feedback/tests/helpers.ts +++ b/packages/feedback/message-feedback/tests/helpers.ts @@ -186,7 +186,6 @@ class TestPersistence extends SessionPersistence { } persist(session: Session): void { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. this.durable.set(session.id, { meta: session.header, events: [...session.snapshotEvents()] }) } diff --git a/packages/feedback/message-feedback/tests/message-feedback.spec.ts b/packages/feedback/message-feedback/tests/message-feedback.spec.ts index 039c0b1388..7a94fefca2 100644 --- a/packages/feedback/message-feedback/tests/message-feedback.spec.ts +++ b/packages/feedback/message-feedback/tests/message-feedback.spec.ts @@ -62,7 +62,6 @@ describe('MessageFeedbackService public contract', () => { }) const fixture = messageFixture('corrupt-session') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. persistence.setDurable({ meta: fixture.session.header, events: fixture.session.snapshotEvents() }) const corruption = new Error('stored log checksum mismatch') persistence.readFailure = corruption @@ -415,7 +414,6 @@ describe('MessageFeedbackService item concurrency', () => { const replacement = Session.create( old.session.id, - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. old.session.snapshotEvents(), { ...old.session.header, createdAt: 20, cwd: '/new' }, ) @@ -495,7 +493,6 @@ describe('canonical message feedback history', () => { const sessionId = fixture.session.id const messageId = fixture.assistantMessageIds[0] persistence.persist(fixture.session) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const prefix = fixture.session.snapshotEvents() const lifecycle: string[] = [] ctx.on('session/created', () => { lifecycle.push('created') }) @@ -541,7 +538,6 @@ describe('canonical message feedback history', () => { const { ctx, persistence } = await harness() const session = ctx.sessions.create(SessionId('live-log')) const fixture = appendMessageFixture(session) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents().length const failure = new Error('disk unavailable') let fail = true @@ -556,9 +552,7 @@ describe('canonical message feedback history', () => { const item = listed.value.items[0]! fail = false expectItem(await ctx.messageFeedback.put({ ...request, ifVersion: item.version })) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(before + 1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(persistence.durable.get(session.id)?.events).toEqual(session.snapshotEvents()) expect(persistence.openCalls).toEqual(['read']) }) @@ -569,7 +563,6 @@ describe('canonical message feedback history', () => { const session = ctx.sessions.create(SessionId('mismatched-checkpoint')) const fixture = appendMessageFixture(session) ctx.on('session/flush', () => { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = [...session.snapshotEvents()] if (kind === 'missing-tail') events.pop() if (kind === 'different-tail') events[events.length - 1] = { ...events.at(-1)!, time: 0 } @@ -594,7 +587,6 @@ describe('canonical message feedback history', () => { expectItem(await ctx.messageFeedback.put({ sessionId: session.id, messageId: fixture.assistantMessageIds[0], rating: 'positive', ifVersion: null, })) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(persistence.durable.get(session.id)!.events.length).toBe(session.snapshotEvents().length - 1) }) @@ -632,7 +624,6 @@ describe('canonical message feedback history', () => { ])('rejects malformed durable feedback payload %#', async (record) => { const { ctx, persistence } = await harness() const fixture = messageFixture('invalid-feedback') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = fixture.session.snapshotEvents() persistence.setDurable({ meta: fixture.session.header, events: [...events, { ...record, seq: SessionSeq(events.length), time: 1, diff --git a/packages/fs/tool-fs/tests/fs-tools.e2e.ts b/packages/fs/tool-fs/tests/fs-tools.e2e.ts index 79bddaa090..9e15589025 100644 --- a/packages/fs/tool-fs/tests/fs-tools.e2e.ts +++ b/packages/fs/tool-fs/tests/fs-tools.e2e.ts @@ -43,7 +43,6 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('fs tools with-key smoke', () => expect(content).not.toContain('draft') // The log records real read/write/edit tool calls (not bash). - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const calls = agent.session.snapshotEvents().filter(e => e.type === 'tool/call').map(e => e.data.name) expect(calls).toContain('write') expect(calls).toContain('read') diff --git a/packages/fs/tool-present/tests/present.spec.ts b/packages/fs/tool-present/tests/present.spec.ts index 4c805c2395..95606e09dc 100644 --- a/packages/fs/tool-present/tests/present.spec.ts +++ b/packages/fs/tool-present/tests/present.spec.ts @@ -84,7 +84,6 @@ describe('present', () => { if (result.isError) throw new Error('present failed') const files = (result.value as unknown as { files: PresentedFile[] }).files expect(files).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().find(event => event.type === 'deliverables/presented')?.data.files).toEqual(files) expect(files).toEqual([{ path: '报告.docx', description: 'Report' }]) expect(read).not.toHaveBeenCalled() @@ -110,7 +109,6 @@ describe('present', () => { execute: async () => ({ turn: 1, files: [] }), })) expect((await execute([])).isError).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().filter(event => event.type === 'deliverables/presented')).toEqual([]) }) @@ -119,7 +117,6 @@ describe('present', () => { await owner.ctx.plugin(Present, { maxFiles: 2 }) await writeFile(join(root, 'a'), 'a') expect((await execute([{ path: 'a' }])).isError).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const deliveries = owner.session.snapshotEvents().filter(event => event.type === 'deliverables/presented') expect(deliveries).toHaveLength(1) expect(deliveries[0]?.data.files[0]?.path).toBe('a') @@ -133,7 +130,6 @@ describe('present', () => { return { kind: 'block', feedback: [{ type: 'text', text: 'blocked' }] } }) expect((await execute([{ path: 'a' }])).isError).toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().some(event => event.type === 'deliverables/presented')).toBe(false) }) @@ -145,7 +141,6 @@ describe('present', () => { const result = await execute(files) expect(result.isError, JSON.stringify(files)).toBe(true) } - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().some(event => event.type === 'deliverables/presented')).toBe(false) }) diff --git a/packages/goal/command-goal/tests/command-goal.spec.ts b/packages/goal/command-goal/tests/command-goal.spec.ts index 94f42cdfe8..1c1761cf74 100644 --- a/packages/goal/command-goal/tests/command-goal.spec.ts +++ b/packages/goal/command-goal/tests/command-goal.spec.ts @@ -59,19 +59,15 @@ async function harness(): Promise { /** The log with executor-owned command lifecycle bookkeeping stripped (goal assertions target domain events). */ function domainEvents(session: Session): readonly SessionEvent[] { const lifecycle = new Set() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of session.snapshotEvents()) { if (event.type !== 'command/run' && event.type !== 'command/done') continue lifecycle.add(event.seq) // The zero-step wrap around a lifecycle event is bookkeeping too. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents()[event.seq - 1] - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const after = session.snapshotEvents()[event.seq + 1] if (before?.type === 'turn/start') lifecycle.add(before.seq) if (after?.type === 'turn/end') lifecycle.add(after.seq) } - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents().filter(event => !lifecycle.has(event.seq)) } diff --git a/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts b/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts index 4732334ddc..2ec805d71b 100644 --- a/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts +++ b/packages/goal/goal-round-driver/tests/goal-round-driver.spec.ts @@ -197,7 +197,6 @@ describe('same-session goal driving', () => { }) expect(test.adapter.requests).toHaveLength(2) const rounds: number[] = [] - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of test.agent.session.snapshotEvents()) { // Round zero is a durable goal state change; positive rounds are the // admitted continuation prompts this test counts. @@ -208,7 +207,6 @@ describe('same-session goal driving', () => { expect(rounds).toEqual([1, 2]) expect(requestText(test.adapter.requests[0]!)).toContain('Round: 1/2') expect(requestText(test.adapter.requests[1]!)).toContain('Round: 2/2') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().flatMap(event => event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series']) }) @@ -264,7 +262,6 @@ describe('same-session goal driving', () => { message: 'Goal round was rejected before entering its step.', }) expect(test.adapter.requests).toHaveLength(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true) }) @@ -302,7 +299,6 @@ describe('same-session goal driving', () => { expect(test.adapter.requests).toHaveLength(0) // No admitted continuation round reached the model; goal state changes are // represented by their own durable event. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'user/message' && event.data.source.kind === 'goal' && event.data.source.round > 0)).toBe(false) }) @@ -381,7 +377,6 @@ describe('same-session goal driving', () => { expect(goal).toMatchObject({ phase: 'paused', roundsStarted: 1 }) expect(test.adapter.requests).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEndKinds = test.agent.session.snapshotEvents().flatMap(event => event.type === 'turn/end' ? [event.data.reason.kind] : []) expect(turnEndKinds).toContain('completed') @@ -399,7 +394,6 @@ describe('same-session goal driving', () => { expect(requestText(test.adapter.requests[0]!)).toContain('human goes first') expect(requestText(test.adapter.requests[0]!)).not.toContain('') expect(requestText(test.adapter.requests[1]!)).toContain('') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().flatMap(event => event.type === 'request/header' ? [event.data.reason] : [])).toEqual(['initial', 'series']) }) @@ -437,7 +431,6 @@ describe('same-session goal driving', () => { const goal = await waitForGoal(test.ctx, test.agent, current => current?.phase === 'blocked') expect(goal).toMatchObject({ revision: 3, objective: 'new objective', roundsStarted: 1 }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const admitted = test.agent.session.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'goal' && event.data.source.round > 0) expect(admitted?.type === 'user/message' && admitted.data.source.kind === 'goal' @@ -824,7 +817,6 @@ describe('same-session goal driving', () => { await test.agent.whenIdle() expect(test.adapter.requests).toHaveLength(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true) }) @@ -999,7 +991,6 @@ describe('same-session goal driving', () => { }) handle.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'one ordinary turn' }], source: { kind: 'user' } })) await handle.agent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const closed = handle.agent.session.snapshotEvents().findLast(event => event.type === 'turn/end') if (closed?.type !== 'turn/end') throw new Error('expected a closed turn') await handle.dispose() @@ -1105,7 +1096,6 @@ describe('same-session goal driving', () => { expect(test.ctx.goals.get(test.agent)).toMatchObject({ phase: 'active', roundsStarted: 0 }) expect(test.adapter.requests).toHaveLength(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(true) }) diff --git a/packages/goal/goal/tests/goal.spec.ts b/packages/goal/goal/tests/goal.spec.ts index 7281859055..ac32cd3765 100644 --- a/packages/goal/goal/tests/goal.spec.ts +++ b/packages/goal/goal/tests/goal.spec.ts @@ -27,7 +27,6 @@ const sessionStubs = new WeakMap() /** Number the next balanced test-fixture turn. */ function nextTurn(session: Session): number { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents().reduce((max, event) => event.type === 'turn/start' ? Math.max(max, event.data.turn) : max, 0) + 1 } @@ -132,9 +131,7 @@ describe('GoalService creation and replay', () => { }) expect(goal.id).toMatch(/^goal-/) expect(seen).toEqual(['create']) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual(['goal/change']) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const context = session.snapshotEvents()[0] expect(context?.type).toBe('goal/change') if (context?.type !== 'goal/change') throw new Error('expected durable goal change') @@ -143,7 +140,6 @@ describe('GoalService creation and replay', () => { expect(change).toMatchObject({ operation: 'create', goal: { id: goal.id } }) expect(agent.inbox.nextStep).toEqual([]) expect(session.deriveMessages()).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { id: goal.id }, roundsStarted: 0 }) vi.useRealTimers() }) @@ -196,7 +192,6 @@ describe('GoalService creation and replay', () => { await ctx.plugin(AgentRegistry) await ctx.plugin(SessionProjectionRegistry) await ctx.plugin(GoalService) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = stubAgent('seeded-goal', first.session.snapshotEvents()) ctx.agents.register(resumed.agent) expect(ctx.goals.get(resumed.agent)).toMatchObject({ @@ -245,14 +240,12 @@ describe('GoalService creation and replay', () => { expect(activations.map(entry => entry.activation)).toEqual(['armed', 'disarmed', 'armed']) expect(activations.map(entry => entry.id)).toEqual([goal.id, goal.id, goal.id]) expect(activations.map(entry => entry.revision)).toEqual([1, 1, 2]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).not.toThrow() }) it('lets a lifecycle owner disarm without writing a durable revision', async () => { const { ctx, agent, session } = await harness() const goal = ctx.goals.create(agent, { objective: 'survive driver reload' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents().length expect(ctx.goals.disarm(agent)).toMatchObject({ id: goal.id, @@ -260,7 +253,6 @@ describe('GoalService creation and replay', () => { phase: 'active', activation: 'disarmed', }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(before) expect(ctx.goals.resume(agent, goal)).toMatchObject({ revision: 2, activation: 'armed' }) }) @@ -424,7 +416,6 @@ describe('GoalService mutations', () => { const tombstone = ctx.goals.clear(agent, goal) expect(tombstone).toEqual({ id: goal.id, revision: 2 }) expect(ctx.goals.get(agent)).toBeUndefined() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0, lastRef: tombstone }) expect(() => ctx.goals.clear(agent, goal)).toThrow(expect.objectContaining({ code: 'GOAL_NOT_FOUND' })) const next = ctx.goals.create(agent, { objective: 'fresh' }) @@ -441,13 +432,11 @@ describe('GoalService mutations', () => { expect(goal.updatedAt).toBe(100) vi.setSystemTime(80) ctx.goals.clear(agent, goal) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const clear = session.snapshotEvents() .filter(event => event.type === 'goal/change') .map(event => event.type === 'goal/change' ? decodeGoalChange(event.data) : undefined) .at(-1) expect(clear).toMatchObject({ operation: 'clear', clearedAt: 100 }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).not.toThrow() vi.useRealTimers() }) @@ -469,12 +458,10 @@ describe('GoalService mutations', () => { goal = ctx.goals.edit(agent, goal, { objective: 'deferred edit' }) goal = ctx.goals.pause(agent, goal) expect(goal).toMatchObject({ revision: 3, phase: 'paused', activation: 'disarmed' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual([ 'goal/change', 'goal/change', 'goal/change', ]) expect(ctx.goals.get(agent)).toMatchObject({ revision: 3, phase: 'paused' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { revision: 3, phase: 'paused' } }) }) @@ -495,7 +482,6 @@ describe('GoalService mutations', () => { expect(observed).toEqual(created) expect(ctx.goals.get(stub.agent)).toEqual(created) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(stub.session.snapshotEvents())).toMatchObject({ goal: { id: created.id, revision: 1 } }) }) @@ -513,7 +499,6 @@ describe('GoalService mutations', () => { revision: 1, }) expect(stub.agent.inbox.nextStep).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(stub.session.snapshotEvents().map(event => event.type)).toEqual(['goal/change']) }) @@ -599,7 +584,6 @@ describe('goal replay validation', () => { function oneChange(change: GoalChangeMeta) { const session = Session.create(SessionId(`validation-${Math.random()}`)) appendChange(session, change) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents() } @@ -631,7 +615,6 @@ describe('goal replay validation', () => { const change = snapshotChange() const session = Session.create(SessionId('inbox-independent-change')) appendChange(session, change) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { id: change.goal.id, revision: 1 } }) const message = createUserMessage({ content: [{ type: 'text', text: 'unrelated pending context' }], @@ -640,7 +623,6 @@ describe('goal replay validation', () => { const inbox = stubAgentForSession(session).agent.inbox inbox.append('next-step', message) expect(inbox.remove(message.id)).toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toMatchObject({ goal: { id: change.goal.id, revision: 1 } }) }) @@ -648,7 +630,6 @@ describe('goal replay validation', () => { const session = Session.create(SessionId(`validation-pair-${Math.random()}`)) appendChange(session, first) appendChange(session, second) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return foldGoal(session.snapshotEvents()) } @@ -660,7 +641,6 @@ describe('goal replay validation', () => { content: [{ type: 'text', text: 'other' }], source: { kind: 'plugin', plugin: 'test' }, })) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0 }) const source = { kind: 'plugin', plugin: 'ordinary-user-message' } as const const turn = nextTurn(session) @@ -669,7 +649,6 @@ describe('goal replay validation', () => { content: [{ type: 'text', text: 'ordinary' }], source, }), { surfaceOp: 'append' }) session.append('turn/end', { turn, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0 }) }) @@ -677,7 +656,6 @@ describe('goal replay validation', () => { const change = snapshotChange() const session = Session.create(SessionId('other-goal-round'), oneChange(change)) appendRound(session, { id: GoalId('goal-other'), revision: 1 }, 1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).toThrow('not the next admitted round') }) @@ -754,7 +732,6 @@ describe('goal replay validation', () => { appendRound(session, base.goal, 2) appendChange(session, { ...paused, roundsStarted: 2 }) appendChange(session, exhausted) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).toThrow('exhausted round budget') }) @@ -779,7 +756,6 @@ describe('goal replay validation', () => { appendChange(completedSession, base) appendChange(completedSession, complete) appendChange(completedSession, sameCurrentId) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(completedSession.snapshotEvents())).toThrow('fresh active revision-one') const second = snapshotChange({ @@ -794,7 +770,6 @@ describe('goal replay validation', () => { appendChange(nonAdjacentReuse, second) appendChange(nonAdjacentReuse, secondComplete) appendChange(nonAdjacentReuse, { ...sameCurrentId, createdAt: 30, updatedAt: 30 }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(nonAdjacentReuse.snapshotEvents())).toThrow('fresh active revision-one') const clear: GoalChangeMeta = { @@ -804,7 +779,6 @@ describe('goal replay validation', () => { appendChange(clearedSession, base) appendChange(clearedSession, clear) appendChange(clearedSession, sameCurrentId) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(clearedSession.snapshotEvents())).toThrow('fresh active revision-one') }) @@ -817,7 +791,6 @@ describe('goal replay validation', () => { content: [{ type: 'text', text: 'missing' }], source, }), { surfaceOp: 'append' }) session.append('turn/end', { turn, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(() => foldGoal(session.snapshotEvents())).toThrow('goal message source is invalid') }) @@ -865,7 +838,6 @@ describe('goal replay validation', () => { clearedAt: 20, } appendChange(session, clear) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(session.snapshotEvents())).toEqual({ roundsStarted: 0, lastRef: { id: change.goal.id, revision: 2 }, diff --git a/packages/goal/goal/tests/projection.spec.ts b/packages/goal/goal/tests/projection.spec.ts index 77c2e23818..45cd1ddb37 100644 --- a/packages/goal/goal/tests/projection.spec.ts +++ b/packages/goal/goal/tests/projection.spec.ts @@ -137,7 +137,6 @@ describe('goal projection unit', () => { }) expect(bench.tailValues().goal).toBeNull() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldGoal(bench.session.snapshotEvents()).goal).toBeUndefined() }) diff --git a/packages/goal/tool-goal/tests/tool-goal.spec.ts b/packages/goal/tool-goal/tests/tool-goal.spec.ts index 00b2feabe2..b4b4d54808 100644 --- a/packages/goal/tool-goal/tests/tool-goal.spec.ts +++ b/packages/goal/tool-goal/tests/tool-goal.spec.ts @@ -68,7 +68,6 @@ function stubAgent(rawId: string, supplied?: Session, suppliedCtx?: Context): St /** Open one message-triggered turn with its accepted model-visible input. */ function openTurn(stub: StubAgent, source: MessageSource, text = 'prompt'): number { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turn = stub.session.snapshotEvents() .filter(event => event.type === 'turn/start') .reduce((max, event) => Math.max(max, event.data.turn), 0) + 1 @@ -283,7 +282,6 @@ describe('goal tool execution authority', () => { const created = ctx.goals.create(root.agent, { objective: 'resume the fork' }) closeTurn(root, originalTurn) const forkId = SessionId('goal-tool-resumed-fork') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const forkSession = Session.create(forkId, root.session.snapshotEvents(), { version: SESSION_FORMAT_VERSION, id: forkId, diff --git a/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts b/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts index e83b99d1ec..4c505d6cf0 100644 --- a/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts +++ b/packages/guard/repeat-tool-reminder/tests/repeat-tool-reminder.spec.ts @@ -37,7 +37,6 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { /** Every injected-context user message in the agent's log, flattened to joined text + source for terse assertions. */ function reminders(agent: Agent): { text: string; source: unknown }[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() .filter((e): e is SessionEvent<'user/message'> => e.type === 'user/message' && e.data.source.kind !== 'user') .map(e => ({ @@ -338,7 +337,6 @@ describe('fold onto the downstream decision', () => { expect(found[1]!.source).toEqual(guardSource('probe', 2)) expect(found[2]).toEqual({ text: 'downstream-ctx', source: { kind: 'plugin', plugin: 'test' } }) // The block's feedback reached the tool result unchanged. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = agent.session.snapshotEvents().filter((e): e is SessionEvent<'tool/result'> => e.type === 'tool/result') expect(results.every(r => r.data.message.content[0].isError)).toBe(true) expect(results[1]!.data.message.content[0].content).toEqual([{ type: 'text', text: 'nope' }]) @@ -363,7 +361,6 @@ describe('fold onto the downstream decision', () => { const found = reminders(agent) expect(found).toHaveLength(1) expect(found[0]!.text).toContain('repeating the exact same tool call') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = agent.session.snapshotEvents().filter((e): e is SessionEvent<'tool/result'> => e.type === 'tool/result') expect(results[1]!.data.message.content[0].content).toEqual([{ type: 'text', text: 'replaced' }]) }) diff --git a/packages/hooks/hook-protocol/tests/events.spec.ts b/packages/hooks/hook-protocol/tests/events.spec.ts index 5da0f91911..45aeca1050 100644 --- a/packages/hooks/hook-protocol/tests/events.spec.ts +++ b/packages/hooks/hook-protocol/tests/events.spec.ts @@ -12,7 +12,6 @@ describe('hook/* session events', () => { const session = Session.create(SessionId('s')) appendHookInvoked(session, { turn: 1, point: 'PreToolUse', dialect: 'claude-code', handlerId: 'h1', matcher: 'Bash' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/invoked') expect(ev?.type).toBe('hook/invoked') if (ev?.type === 'hook/invoked') { @@ -26,7 +25,6 @@ describe('hook/* session events', () => { const session = Session.create(SessionId('s')) appendHookInvoked(session, { turn: 2, point: 'Stop', dialect: 'codex', handlerId: 'h2' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/invoked') if (ev?.type === 'hook/invoked') { expect('matcher' in ev.data).toBe(false) @@ -39,7 +37,6 @@ describe('hook/* session events', () => { turn: 1, point: 'PreToolUse', handlerId: 'h1', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: 2, stderr: 'blocked', decision: 'deny' }), }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const full = session.snapshotEvents().find(e => e.type === 'hook/result') if (full?.type === 'hook/result') { expect(full.data).toEqual({ turn: 1, point: 'PreToolUse', handlerId: 'h1', decision: 'deny', exitCode: 2, stderrSummary: 'blocked', durationMs: 5 }) @@ -51,7 +48,6 @@ describe('hook/* session events', () => { turn: 1, point: 'Stop', handlerId: 'h3', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: undefined, decision: 'allow' }), }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const sparse = session2.snapshotEvents().find(e => e.type === 'hook/result') if (sparse?.type === 'hook/result') { expect('exitCode' in sparse.data).toBe(false) @@ -67,7 +63,6 @@ describe('hook/* session events', () => { // An explicit decision wins over the continue:false fallback. appendHookResult(session, { turn: 1, point: 'Stop', handlerId: 'both', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ continue: false, decision: 'block' }) }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decisions = session.snapshotEvents() .filter(e => e.type === 'hook/result') .map(e => e.type === 'hook/result' ? [e.data.handlerId, e.data.decision] : []) @@ -80,7 +75,6 @@ describe('hook/* session events', () => { turn: 1, point: 'PreToolUse', handlerId: 'long', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: 2, stderr: ` ${'x'.repeat(600)} ` }), }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/result') if (ev?.type === 'hook/result') { expect(ev.data.stderrSummary).toBe('x'.repeat(500) + '…') @@ -93,7 +87,6 @@ describe('hook/* session events', () => { turn: 1, point: 'PreToolUse', handlerId: 'edge', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ exitCode: 2, stderr: 'y'.repeat(500) }), }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const ev = session.snapshotEvents().find(e => e.type === 'hook/result') if (ev?.type === 'hook/result') { expect(ev.data.stderrSummary).toBe('y'.repeat(500)) @@ -105,9 +98,7 @@ describe('hook/* session events', () => { appendHookInvoked(session, { turn: 1, point: 'PreToolUse', dialect: 'claude-code', handlerId: 'pair-1' }) appendHookResult(session, { turn: 1, point: 'PreToolUse', handlerId: 'pair-1', stderrSummaryMaxChars: 500, durationMs: 5, output: output({ decision: 'allow' }) }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const invoked = session.snapshotEvents().find(e => e.type === 'hook/invoked') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const result = session.snapshotEvents().find(e => e.type === 'hook/result') expect(invoked?.type === 'hook/invoked' && invoked.data.handlerId).toBe('pair-1') expect(result?.type === 'hook/result' && result.data.handlerId).toBe('pair-1') diff --git a/packages/hooks/hooks-claude-code/tests/bridge.spec.ts b/packages/hooks/hooks-claude-code/tests/bridge.spec.ts index ec6a9c2453..68f6c8319d 100644 --- a/packages/hooks/hooks-claude-code/tests/bridge.spec.ts +++ b/packages/hooks/hooks-claude-code/tests/bridge.spec.ts @@ -71,7 +71,6 @@ function waitForIdle(_ctx: Context, agent: Agent): Promise { } function events(agent: Agent): readonly SessionEvent[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() } diff --git a/packages/hooks/hooks-claude-code/tests/coverage-cases.ts b/packages/hooks/hooks-claude-code/tests/coverage-cases.ts index 1edfcb2500..8627a970cd 100644 --- a/packages/hooks/hooks-claude-code/tests/coverage-cases.ts +++ b/packages/hooks/hooks-claude-code/tests/coverage-cases.ts @@ -52,7 +52,6 @@ async function harness(configPath: string, adapter: MockAdapter, opts: HarnessOp function waitForIdle(_ctx: Context, agent: Agent): Promise { return agent.whenIdle() } -// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. function events(agent: Agent): readonly SessionEvent[] { return agent.session.snapshotEvents() } /** Poll until `predicate` holds or the deadline passes — robust to detached * emit-listener hooks firing on a `.then` (a fixed sleep flakes under load). */ diff --git a/packages/hooks/hooks-codex/tests/bridge.spec.ts b/packages/hooks/hooks-codex/tests/bridge.spec.ts index 2ffb062a0c..1ef87de7c1 100644 --- a/packages/hooks/hooks-codex/tests/bridge.spec.ts +++ b/packages/hooks/hooks-codex/tests/bridge.spec.ts @@ -54,7 +54,6 @@ async function harness(dir: string, adapter: MockAdapter, beforeHooks?: (ctx: Co function waitForIdle(_ctx: Context, agent: Agent): Promise { return agent.whenIdle() } -// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. function events(agent: Agent): readonly SessionEvent[] { return agent.session.snapshotEvents() } /** Poll `predicate` until true or the deadline passes (detached hook effects can't be awaited directly). */ diff --git a/packages/hooks/hooks-codex/tests/coverage-cases.ts b/packages/hooks/hooks-codex/tests/coverage-cases.ts index 91956f33a5..12a3c370ab 100644 --- a/packages/hooks/hooks-codex/tests/coverage-cases.ts +++ b/packages/hooks/hooks-codex/tests/coverage-cases.ts @@ -42,7 +42,6 @@ async function harness(configPath: string, adapter: MockAdapter, opts: HarnessOp function waitForIdle(_ctx: Context, agent: Agent): Promise { return agent.whenIdle() } -// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. function events(agent: Agent): readonly SessionEvent[] { return agent.session.snapshotEvents() } /** Poll until `predicate` holds or the deadline passes — robust to detached * emit-listener hooks firing on a `.then` (a fixed sleep flakes under load). */ diff --git a/packages/interaction/commands/tests/commands.spec.ts b/packages/interaction/commands/tests/commands.spec.ts index aef91060c8..92a12d921d 100644 --- a/packages/interaction/commands/tests/commands.spec.ts +++ b/packages/interaction/commands/tests/commands.spec.ts @@ -33,7 +33,6 @@ async function mintAgentScope(ctx: Context, name: string): Promise<{ scope: Scop /** The lifecycle slice of one agent's log (boundary markers stripped). */ function lifecycleOf(agent: Agent): Array<{ type: string; data: unknown }> { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() .filter(event => event.type === 'command/run' || event.type === 'command/done') .map(event => ({ type: event.type, data: event.data })) @@ -317,7 +316,6 @@ describe('CommandRuntime', () => { // The execution's pairing id is the logged one (RPC-level correlation). expect(execution?.commandId).toBe(ids[0]) // Direct log-only appends: no turn is opened for the pair on an idle log. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().map(event => event.type)).toEqual([ 'command/run', 'command/done', ]) @@ -356,7 +354,6 @@ describe('CommandRuntime', () => { await ctx.commands.execute(agent, '/private keep this once', [], new AbortController().signal) expect(seen).toHaveBeenCalledWith(expect.objectContaining({ rawInput: ' keep this once' })) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const run = agent.session.snapshotEvents().find(event => event.type === 'command/run') expect(run?.type).toBe('command/run') expect(run?.type === 'command/run' && Object.hasOwn(run.data, 'args')).toBe(false) @@ -431,7 +428,6 @@ describe('CommandRuntime', () => { const signal = new AbortController().signal await ctx.commands.execute(agent, 'not a command', [], signal) await ctx.commands.execute(agent, '/missing', [], signal) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents()).toEqual([]) }) @@ -441,7 +437,6 @@ describe('CommandRuntime', () => { ctx.commands.register(command('mid')) agent.session.append('turn/start', { turn: 1 }) await ctx.commands.execute(agent, '/mid', [], new AbortController().signal) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().map(event => event.type)).toEqual([ 'turn/start', 'command/run', 'command/done', ]) diff --git a/packages/interaction/permission-presets/tests/permission-presets.spec.ts b/packages/interaction/permission-presets/tests/permission-presets.spec.ts index 552373474f..3da5749e2b 100644 --- a/packages/interaction/permission-presets/tests/permission-presets.spec.ts +++ b/packages/interaction/permission-presets/tests/permission-presets.spec.ts @@ -151,7 +151,6 @@ describe('PermissionPresetService', () => { const ctx = await mounted() const session = freshSession('sess-set') ctx.permissionPresets.set(session, 'danger-full-access') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(e => [e.type, e.data])).toEqual([ ['permission/preset', { preset: 'danger-full-access' }], ['sandbox/mode', { mode: 'danger-full-access' }], @@ -163,7 +162,6 @@ describe('PermissionPresetService', () => { const ctx = await mounted() const session = freshSession('sess-noop') ctx.permissionPresets.set(session, 'workspace-write') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(0) }) @@ -175,7 +173,6 @@ describe('PermissionPresetService', () => { // the changed knob. session.append('sandbox/mode', { mode: 'read-only' }) ctx.permissionPresets.set(session, 'danger-full-access') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const tail = session.snapshotEvents().slice(4) expect(tail.map(e => [e.type, e.data])).toEqual([ ['permission/preset', { preset: 'danger-full-access' }], @@ -211,7 +208,6 @@ describe('PermissionPresetService', () => { const ctx = await mounted({ approvalDefault: undefined }) const session = freshSession('sess-standin') ctx.permissionPresets.set(session, 'workspace-write') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(0) expect(ctx.permissionPresets.current(session)).toBe('workspace-write') }) @@ -221,7 +217,6 @@ describe('new-session default', () => { it('pins the current setting into each new session without changing earlier sessions', async () => { const ctx = await mountedStore() const first = ctx.sessions.create(SessionId('first')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(first.snapshotEvents().map(event => [event.type, event.data])).toEqual([ ['permission/preset', { preset: 'workspace-write' }], ['sandbox/mode', { mode: 'workspace-write' }], @@ -235,7 +230,6 @@ describe('new-session default', () => { const second = ctx.sessions.create(SessionId('second')) expect(ctx.permissionPresets.current(first)).toBe('workspace-write') expect(ctx.permissionPresets.current(second)).toBe('danger-full-access') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(second.snapshotEvents().map(event => event.type)).toEqual([ 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -249,10 +243,8 @@ describe('new-session default', () => { const legacy = freshSession('legacy-source') legacy.append('turn/start', { turn: 1 }) legacy.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = ctx.sessions.create(SessionId('legacy-resumed'), { seed: legacy.snapshotEvents() }) expect(ctx.permissionPresets.current(resumed)).toBe('workspace-write') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().slice(-3).map(event => event.type)).toEqual([ 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -265,7 +257,6 @@ describe('new-session default', () => { }) const resumed = ctx.sessions.create(SessionId('empty-resumed'), { seed: [] }) expect(ctx.permissionPresets.current(resumed)).toBe('workspace-write') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().map(event => event.type)).toEqual([ 'session/end-seed', 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -283,11 +274,9 @@ describe('new-session default', () => { }) ctx.provide('approval', { config: { policy: 'ask' } }) const existing = ctx.sessions.create(SessionId('existing-before-permission')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(existing.snapshotEvents()).toEqual([]) await ctx.plugin(PermissionPresetService, {}) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(existing.snapshotEvents().map(event => event.type)).toEqual([ 'permission/preset', 'sandbox/mode', 'approval/policy', ]) @@ -313,7 +302,6 @@ describe('new-session default', () => { // The remount sweep must read the folded knob events instead of treating // the session as fresh; no default preset events may overwrite the // overrides (read-only + never matches no preset table entry). - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(existing.snapshotEvents().map(event => event.type)).toEqual([ 'sandbox/mode', 'approval/policy', ]) @@ -325,9 +313,7 @@ describe('new-session default', () => { const partial = freshSession('partial-source') partial.append('sandbox/mode', { mode: 'workspace-write' }) partial.append('approval/policy', { policy: 'ask' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = ctx.sessions.create(SessionId('partial-resumed'), { seed: partial.snapshotEvents() }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().at(-1)).toMatchObject({ type: 'permission/preset', data: { preset: 'workspace-write' }, @@ -336,10 +322,8 @@ describe('new-session default', () => { const custom = freshSession('custom-source') custom.append('sandbox/mode', { mode: 'read-only' }) custom.append('approval/policy', { policy: 'never' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const unmatched = ctx.sessions.create(SessionId('custom-resumed'), { seed: custom.snapshotEvents() }) expect(ctx.permissionPresets.current(unmatched)).toBe(CUSTOM_PRESET) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(unmatched.snapshotEvents().at(-1)?.type).toBe('session/end-seed') }) @@ -347,9 +331,7 @@ describe('new-session default', () => { const ctx = await mountedStore({ approvalDefault: undefined }) const partial = freshSession('approval-fallback-source') partial.append('sandbox/mode', { mode: 'workspace-write' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = ctx.sessions.create(SessionId('approval-fallback-resumed'), { seed: partial.snapshotEvents() }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(resumed.snapshotEvents().at(-1)).toMatchObject({ type: 'approval/policy', data: { policy: 'ask' }, diff --git a/packages/interaction/permission-presets/tests/projection.spec.ts b/packages/interaction/permission-presets/tests/projection.spec.ts index 9a7ef5d0d3..53022e9089 100644 --- a/packages/interaction/permission-presets/tests/projection.spec.ts +++ b/packages/interaction/permission-presets/tests/projection.spec.ts @@ -99,7 +99,6 @@ describe('/permission command', () => { text: 'The approval policy changed from "ask" to "never" (changed by the user).', }], }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const run = session.snapshotEvents().find(event => event.type === 'command/run') expect(run?.data).toMatchObject({ name: 'permission', args: ' danger-full-access' }) }) @@ -112,14 +111,12 @@ describe('/permission command', () => { kind: 'success', text: 'current preset workspace-write (available: workspace-write, danger-full-access)', }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'permission/preset')).toHaveLength(1) }) it('rejects an unknown preset without touching the log', async () => { const { ctx, session } = await harness() const { agent } = await agentFor(ctx, session) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents().filter(event => event.type !== 'command/run' && event.type !== 'command/done') const execution = await ctx.commands.execute(agent, '/permission yolo', [], new AbortController().signal) @@ -130,7 +127,6 @@ describe('/permission command', () => { kind: 'error', text: 'unknown preset "yolo" (available: workspace-write, danger-full-access)', }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type !== 'command/run' && event.type !== 'command/done')).toEqual(before) }) diff --git a/packages/interaction/user-approval/tests/approval.spec.ts b/packages/interaction/user-approval/tests/approval.spec.ts index c0fce76425..70e1abcf4e 100644 --- a/packages/interaction/user-approval/tests/approval.spec.ts +++ b/packages/interaction/user-approval/tests/approval.spec.ts @@ -132,11 +132,8 @@ describe('ApprovalService.request', () => { await expect(ctx.approval.request(requestOf(agent))).resolves.toBe('allowed-once') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const audit = session.snapshotEvents().filter(event => event.type.startsWith('approval/')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const asked = session.snapshotEvents().find((event): event is SessionEvent<'approval/asked'> => event.type === 'approval/asked') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decided = session.snapshotEvents().find((event): event is SessionEvent<'approval/decided'> => event.type === 'approval/decided') expect(audit.map(event => event.type)).toEqual(['approval/asked', 'approval/decided']) expect(decided?.data.id).toBe(asked?.data.id) @@ -158,11 +155,8 @@ describe('ApprovalService.request', () => { await expect(ctx.approval.request(requestOf(agent))).resolves.toBe('rejected') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const audit = session.snapshotEvents().filter(event => event.type.startsWith('approval/')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const asked = session.snapshotEvents().find((event): event is SessionEvent<'approval/asked'> => event.type === 'approval/asked') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decided = session.snapshotEvents().find((event): event is SessionEvent<'approval/decided'> => event.type === 'approval/decided') expect(audit.map(event => event.type)).toEqual(['approval/asked', 'approval/decided']) expect(decided?.data).toMatchObject({ id: asked?.data.id, outcome: 'rejected' }) @@ -382,7 +376,6 @@ describe('approval policy (the approval/policy fold)', () => { setApprovalPolicy(session, 'never') setApprovalPolicy(session, 'ask') expect(service.overrideOf(session)).toBe('ask') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().at(-1)).toMatchObject({ type: 'approval/policy', data: { policy: 'ask' } }) }) @@ -422,9 +415,7 @@ describe('approval policy (the approval/policy fold)', () => { await expect(ctx.approval.request({ agent, toolName: 'bash' })).resolves.toBe('rejected') expect(consulted).not.toHaveBeenCalled() // The audit pair still lands on the session log. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(e => e.type === 'approval/asked')).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(e => e.type === 'approval/decided')).toHaveLength(1) }) diff --git a/packages/llm/llm-retry/tests/loader-composition.spec.ts b/packages/llm/llm-retry/tests/loader-composition.spec.ts index 7b95f6982d..5c5a177040 100644 --- a/packages/llm/llm-retry/tests/loader-composition.spec.ts +++ b/packages/llm/llm-retry/tests/loader-composition.spec.ts @@ -112,7 +112,6 @@ describe('real Loader composition', () => { await agent.whenIdle() expect(adapter.requests).toBe(2) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'llm/retry')).toHaveLength(1) expect(agent.session.deriveMessages().at(-1)).toMatchObject({ role: 'assistant', diff --git a/packages/llm/llm-retry/tests/retry.spec.ts b/packages/llm/llm-retry/tests/retry.spec.ts index cf61fcd4fa..e7f0ededb2 100644 --- a/packages/llm/llm-retry/tests/retry.spec.ts +++ b/packages/llm/llm-retry/tests/retry.spec.ts @@ -215,7 +215,6 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(2) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(item => item.type === 'step/start').map(item => item.data)) .toEqual([{ turn: 1, step: 1 }]) expect(agent.session.deriveMessages().at(-1)).toEqual({ @@ -251,7 +250,6 @@ describe('provider-routed retry policy', () => { await idle 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 === 'assistant/message').map(event => ({ turn: event.data.turn, step: event.data.step, @@ -288,9 +286,7 @@ describe('provider-routed retry policy', () => { await vi.advanceTimersByTimeAsync(500) await idle - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const retryEvent = agent.session.snapshotEvents().find(event => event.type === 'llm/retry') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const failedAttempts = agent.session.snapshotEvents().filter((event): event is SessionEvent<'assistant/attempt'> => event.type === 'assistant/attempt' && retryEvent !== undefined @@ -298,14 +294,12 @@ describe('provider-routed retry policy', () => { ) expect(failedAttempts).toHaveLength(1) expect(expandAssistantStream(failedAttempts[0]!.data.stream)).toHaveLength(7) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const assistantMessages = agent.session.snapshotEvents().filter(event => event.type === 'assistant/message') expect(assistantMessages.map(event => ({ turn: event.data.turn, step: event.data.step, }))).toEqual([{ turn: 1, step: 1 }]) expect(assistantMessages[0]?.sourceEventSeqs).toBeUndefined() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'tool/call')).toBe(false) expect(toolExecutions).toBe(0) expect(agent.session.deriveMessages().at(-1)).toMatchObject({ @@ -343,9 +337,7 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(3) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'llm/retry')).toHaveLength(2) - // 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', error: { message: 'busy three', code: 'SERVER' } } }, @@ -401,7 +393,6 @@ describe('provider-routed retry policy', () => { rejectedAgent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } })) await rejectedIdle expect(rejected.requests).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(rejectedAgent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) }) @@ -440,7 +431,6 @@ describe('provider-routed retry policy', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } })) await idle expect(adapter.requests).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) expect(vi.getTimerCount()).toBe(0) }) @@ -460,9 +450,7 @@ describe('provider-routed retry policy', () => { await idle 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 === 'llm/retry')).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const end = agent.session.snapshotEvents().at(-1) expect(end).toMatchObject({ type: 'turn/end', @@ -491,7 +479,6 @@ describe('provider-routed retry policy', () => { const normalIdle = waitForIdle(context, normalAgent) normalAgent.followup(createUserMessage({ content: [{ type: 'text', text: 'normal' }], source: { kind: 'user' } })) await normalIdle - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(normalAgent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) const alwaysAgent = await context.agentLoop.create(SessionId('retry-provider-always'), { @@ -578,7 +565,6 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests.map(request => request.provider)).toEqual(['mock', 'other', 'other']) - // 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 => ({ provider: event.data.provider, retry: event.data.retry, @@ -690,7 +676,6 @@ describe('provider-routed retry policy', () => { await vi.runAllTimersAsync() await idle - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = agent.session.snapshotEvents().filter(event => event.type === 'llm/retry') expect(adapter.requests).toHaveLength(5) expect(events.map(event => ({ @@ -735,7 +720,6 @@ describe('provider-routed retry policy', () => { const retriedContext = JSON.stringify(adapter.requests[1]?.messages) expect(retriedContext).not.toContain(diagnostic) expect(retriedContext).not.toContain('discarded partial output') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry' && event.data.failure.message === diagnostic, )).toBe(true) @@ -758,7 +742,6 @@ describe('provider-routed retry policy', () => { await idle 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 === 'llm/retry')).toBe(false) }) @@ -810,7 +793,6 @@ describe('provider-routed retry policy', () => { await vi.advanceTimersByTimeAsync(60_000) 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 === 'step/start')).toHaveLength(1) expect(vi.getTimerCount()).toBe(0) }) @@ -852,7 +834,6 @@ describe('provider-routed retry policy', () => { expect(order[0]).toBe('downstream') expect(order).toEqual(expect.arrayContaining(['disposed', 'idle'])) expect(adapter.requests).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).toBe(false) }) @@ -891,7 +872,6 @@ describe('provider-routed retry policy', () => { expect(order).toEqual(['downstream', 'idle']) expect(adapter.requests).toHaveLength(1) - // 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' } }, @@ -929,7 +909,6 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(1) - // 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' } }, @@ -987,7 +966,6 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(1) - // 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' } }, @@ -1017,9 +995,7 @@ describe('provider-routed retry policy', () => { await idle expect(adapter.requests).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).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: 'aborted' } }, @@ -1043,7 +1019,6 @@ describe('provider-routed retry policy', () => { await idle 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 === 'llm/retry')).toHaveLength(1) expect(vi.getTimerCount()).toBe(0) }) diff --git a/packages/llm/llm-retry/tests/transport-recovery.spec.ts b/packages/llm/llm-retry/tests/transport-recovery.spec.ts index b14db6e47e..ba218842de 100644 --- a/packages/llm/llm-retry/tests/transport-recovery.spec.ts +++ b/packages/llm/llm-retry/tests/transport-recovery.spec.ts @@ -102,11 +102,9 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { expect(server).toBeDefined() expect(server?.requests).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') .map(event => [event.data.turn, event.data.step])) .toEqual([[1, 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.failure.code)) .toEqual(['TRANSPORT']) expect(finalAssistantText(agent)).toBe('connected after retry') @@ -133,9 +131,7 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { expect(server.requests).toHaveLength(2) expect(server.requests[0]?.body).toEqual(server.requests[1]?.body) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const retryEvent = agent.session.snapshotEvents().find(event => event.type === 'llm/retry') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const failedAttempts = agent.session.snapshotEvents().filter((event): event is SessionEvent<'assistant/attempt'> => event.type === 'assistant/attempt' && retryEvent !== undefined @@ -143,11 +139,9 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { ) expect(failedAttempts).toHaveLength(1) expect(expandAssistantStream(failedAttempts[0]!.data.stream)).toHaveLength(failedChunkCount) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'assistant/message') .map(event => [event.data.turn, event.data.step])) .toEqual([[1, 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.failure.code)) .toEqual(['TRANSPORT']) expect(finalAssistantText(agent)).toBe('recovered response') @@ -168,14 +162,11 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { expect(server.requests).toHaveLength(2) expect(server.requests[0]?.body).toEqual(server.requests[1]?.body) - // 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.failure.code)) .toEqual(['EMPTY_RESPONSE']) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'assistant/message') .map(event => [event.data.turn, event.data.step])) .toEqual([[1, 1]]) - // 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' } }, @@ -198,14 +189,10 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { await sendAndWait(context, agent) expect(server.requests).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const attempt = agent.session.snapshotEvents().find(event => event.type === 'assistant/attempt' && event.data.turn === 1) expect(attempt?.type === 'assistant/attempt' ? expandAssistantStream(attempt.data.stream) : []).toHaveLength(3) - // 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) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'llm/retry')).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', error: { message: 'SSE stream ended without [DONE]', code: 'STREAM_CLOSED' } } }, @@ -228,7 +215,6 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { await sendAndWait(context, agent) expect(server.requests.map(record => record.behavior)).toEqual(['stall', 'success']) - // 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.failure.code)) .toEqual(['TIMEOUT']) expect(finalAssistantText(agent)).toBe('recovered after timeout') @@ -247,11 +233,8 @@ describe('bounded retry through the real DeepSeek HTTP/SSE adapter', () => { await sendAndWait(context, agent) expect(server.requests).toHaveLength(3) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'step/start')).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')).toHaveLength(2) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const end = agent.session.snapshotEvents().at(-1) expect(end).toMatchObject({ type: 'turn/end', diff --git a/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts b/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts index 35f64858f9..769f00e0ec 100644 --- a/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts +++ b/packages/llm/token-meter/tests/context-breakdown-projection.spec.ts @@ -292,7 +292,6 @@ describe('contextBreakdown session projection', () => { const first = appendUser(session, 'first message') const last = appendUser(session, 'last message') const definition = contextBreakdownProjectionDefinition - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const state = session.snapshotEvents().reduce(definition.apply, definition.init()) const before = JSON.stringify(state) const replacement = session.append('user/message', createUserMessage({ @@ -383,10 +382,8 @@ describe('contextBreakdown session projection', () => { const { session } = await harness() const head = appendSystem(session, 'head') const definition = contextBreakdownProjectionDefinition - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const state = session.snapshotEvents().reduce(definition.apply, definition.init()) replaceSystem(session, head, 'same') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const next = definition.apply(state, session.snapshotEvents().at(-1)!) expect(next).not.toBe(state) expect(definition.wire.view(next)).toBe(definition.wire.view(state)) @@ -424,7 +421,6 @@ describe('contextBreakdown session projection', () => { stale, [replacement], SessionLogOffset(replacement.seq), session.header, session.inheritedEventCount, )).toThrow('re-read from seq 0') const replayed = ctx.sessionProjections.restore( - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. stale, session.snapshotEvents(), SessionLogOffset(0), session.header, session.inheritedEventCount, ) expect(replayed.snapshot.values.contextBreakdown).toEqual(projected(ctx, session)) @@ -437,7 +433,6 @@ describe('contextBreakdown session projection', () => { }, } expect(() => ctx.sessionProjections.restore( - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. invalid, session.snapshotEvents(), SessionLogOffset(0), session.header, session.inheritedEventCount, )).toThrow() }) @@ -457,7 +452,6 @@ describe('contextBreakdown session projection', () => { expect.soft(ctx.sessionProjections.viewCheckpoint(checkpoint)).not.toHaveProperty('contextBreakdown') expect.soft(ctx.sessionProjections.restoreFloor(checkpoint)).toBe(0) const restored = ctx.sessionProjections.restore( - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. checkpoint, session.snapshotEvents(), SessionLogOffset(0), session.header, session.inheritedEventCount, ) expect(restored.snapshot.values.contextBreakdown).toEqual({ diff --git a/packages/llm/token-meter/tests/token-meter.spec.ts b/packages/llm/token-meter/tests/token-meter.spec.ts index bd8218beb0..e795ea5507 100644 --- a/packages/llm/token-meter/tests/token-meter.spec.ts +++ b/packages/llm/token-meter/tests/token-meter.spec.ts @@ -230,7 +230,6 @@ describe('TokenMeter pricing', () => { const result = service.measure(session) expect(result.baseline.kind).toBe('estimated') expect(result.totalTokens).toBeGreaterThan(result.surfaceTokens) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(result.logRevision).toBe(session.snapshotEvents().length) expectSurfaceTotal(result) }) @@ -424,7 +423,6 @@ describe('replay anchors and surface folds', () => { content: [{ type: 'text', text: 'new tail' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seeded = Session.create(SessionId('surface-seeded'), original.snapshotEvents()) const before = service.measure(seeded) expect(before.nodes).toHaveLength(2) @@ -438,9 +436,7 @@ describe('replay anchors and surface folds', () => { }), { surfaceOp: { op: 'replace', startSeq: first, endSeq: first }, sourceEventSeqs: [first] }) const after = service.measure(seeded) expect(after.nodes).toHaveLength(2) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(after.nodes[0]!.seq).toBe(seeded.snapshotEvents().length - 1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(after.logRevision).toBe(seeded.snapshotEvents().length) expect(Object.isFrozen(after.nodes)).toBe(true) expect(Object.isFrozen(after.nodes[0])).toBe(true) @@ -448,7 +444,6 @@ describe('replay anchors and surface folds', () => { expectSurfaceTotal(after) expect(before.nodes).toHaveLength(2) // The earlier snapshot still reports the log it measured: seed + boundary. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(before.logRevision).toBe(original.snapshotEvents().length + 1) expect(before.surfaceDeltaTokens).toBeGreaterThan(0) }) @@ -460,7 +455,6 @@ describe('replay anchors and surface folds', () => { durableText: '', }) const measurement = meter().measure(session) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const assistant = session.snapshotEvents().find(event => event.type === 'assistant/message')! expect(measurement.nodes).toEqual([{ seq: assistant.seq, tokens: 0, heuristicTokens: 0 }]) expect(measurement.surfaceTokens).toBe(0) diff --git a/packages/plan/plan-mode/tests/integration.spec.ts b/packages/plan/plan-mode/tests/integration.spec.ts index 4b0eb97f01..ac82e45822 100644 --- a/packages/plan/plan-mode/tests/integration.spec.ts +++ b/packages/plan/plan-mode/tests/integration.spec.ts @@ -105,7 +105,6 @@ describe('plan mode through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'explore the repo' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() const planMode = findEvent(log, 'plan/mode') const header = findEvent(log, 'request/header') @@ -139,10 +138,8 @@ describe('plan mode through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'hello' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) expect(planActive(ctx, agent)).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const first = findEvent(agent.session.snapshotEvents(), 'request/header') expect(first.data.header.tools?.map(tool => tool.name)).toEqual(['exit_plan_mode', 'read', 'write']) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const firstSystem = findEvent(agent.session.snapshotEvents(), 'system/message') expect(systemText(agent)).not.toContain(PLAN_CONFIG.section) @@ -150,7 +147,6 @@ describe('plan mode through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'now plan' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() expect(planActive(ctx, agent)).toBe(true) const notices = log.filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin') @@ -200,7 +196,6 @@ describe('plan mode through the agent loop', () => { expect(requestSystem(adapter.requests[1])).not.toContain(PLAN_CONFIG.section) expect(adapter.requests[1]?.tools).toEqual(adapter.requests[0]?.tools) expect(ctx.planMode.get(agent)).toEqual({ active: false, pending: true }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) const nextIdle = waitForIdle(ctx, agent) @@ -210,7 +205,6 @@ describe('plan mode through the agent loop', () => { expect(adapter.requests).toHaveLength(3) expect(requestSystem(adapter.requests[2])).toContain(PLAN_CONFIG.section) expect(adapter.requests[2]?.tools).toEqual(adapter.requests[0]?.tools) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() const planMode = findEvent(log, 'plan/mode') const firstEnd = log.find(event => event.type === 'step/end' diff --git a/packages/plan/plan-mode/tests/plan-mode.spec.ts b/packages/plan/plan-mode/tests/plan-mode.spec.ts index 9bd0e7c87c..2e3bd89949 100644 --- a/packages/plan/plan-mode/tests/plan-mode.spec.ts +++ b/packages/plan/plan-mode/tests/plan-mode.spec.ts @@ -142,7 +142,6 @@ function header(session: Session): void { } function noticeTexts(session: Session): string[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents() .filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin') .map(event => (event.data as { content: { type: string; text?: string }[] }).content.map(block => block.text ?? '').join('')) @@ -206,12 +205,10 @@ describe('resolveConfig', () => { describe('foldPlanMode', () => { it('folds an empty log to inactive and takes the last plan/mode otherwise', () => { const session = Session.create(SessionId('fold')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents())).toBe(false) session.append('plan/mode', { active: true }) session.append('plan/mode', { active: false }) session.append('plan/mode', { active: true }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents())).toBe(true) }) @@ -219,9 +216,7 @@ describe('foldPlanMode', () => { const session = Session.create(SessionId('fold-prefix')) session.append('plan/mode', { active: true }) session.append('plan/mode', { active: false }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents(), 1)).toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(session.snapshotEvents(), 0)).toBe(false) }) }) @@ -286,16 +281,13 @@ describe('ctx.planMode: get/set', () => { const ctx = await setup() const agent = await agentWithSession(ctx, 'agent-idle') expect(ctx.planMode.set(agent, true)).toBe('committed') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent)).toEqual({ active: true }) // Immediately reversible, still without a boundary. expect(ctx.planMode.set(agent, false)).toBe('committed') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) // A later boundary finds nothing pending — no double append. await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().filter(event => event.type === 'plan/mode')).toHaveLength(2) }) @@ -307,7 +299,6 @@ describe('ctx.planMode: get/set', () => { closeTurn(agent.session) // Back to the logged state: the pending intent clears, nothing lands. expect(ctx.planMode.set(agent, false)).toBe('cancelled') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) expect(ctx.planMode.get(agent)).toEqual({ active: false }) }) @@ -328,7 +319,6 @@ describe('the boundary flush', () => { const service = ctx.planMode as unknown as { onBoundary(session: Session): void } expect(() => { service.onBoundary(agent.session) }).not.toThrow() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) }) @@ -338,7 +328,6 @@ describe('the boundary flush', () => { openTurn(agent.session) ctx.planMode.set(agent, true) await boundary(ctx, agent, 'pre-step') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent)).toEqual({ active: true }) }) @@ -354,7 +343,6 @@ describe('the boundary flush', () => { ctx.planMode.set(agent, true) await fiber.dispose() await boundary(ctx, agent, 'pre-step') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) }) @@ -363,7 +351,6 @@ describe('the boundary flush', () => { const agent = await agentWithSession(ctx) ctx.planMode.set(agent, true) await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -375,7 +362,6 @@ describe('the boundary flush', () => { ctx.planMode.set(agent, true) ctx.planMode.set(agent, false) await boundary(ctx, agent, 'pre-step') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) expect(noticeTexts(agent.session)).toEqual([]) }) @@ -417,7 +403,6 @@ describe('the boundary flush', () => { agent.session.append('plan/mode', { active: false }) ctx.planMode.set(agent, true) await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(noticeTexts(agent.session)).toEqual([]) }) @@ -445,7 +430,6 @@ describe('the boundary flush', () => { expect(ctx.planMode.get(agent)).toEqual({ active: false, pending: true }) agent.session.append = original await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent).pending).toBeUndefined() }) @@ -690,7 +674,6 @@ describe('/plan', () => { expect(enteringSteer).not.toHaveBeenCalled() await boundary(ctx, entering, 'step-start') expect(ctx.planMode.get(entering)).toEqual({ active: false }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(entering.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) const active = await agentWithSession(ctx, 'active-plan-command', { active: true }) @@ -715,11 +698,9 @@ describe('/plan', () => { const agent = await agentWithSession(ctx, 'idle-plan-command') expect((await ctx.commands.execute(agent, '/plan', [], signal))?.result) .toEqual({ kind: 'success', text: 'Plan mode on. Use /plan off to leave.' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect((await ctx.commands.execute(agent, '/plan off', [], signal))?.result) .toEqual({ kind: 'success', text: 'Plan mode off.' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) }) @@ -883,7 +864,6 @@ describe('exit_plan_mode', () => { expect(result.content).toEqual([{ type: 'text', text: 'Error: exit_plan_mode requires a non-empty markdown plan starting with a # heading' }]) } expect(asked).toHaveLength(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -893,7 +873,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: no user-questions channel is available to review the plan; ask the user to switch the session mode instead' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -902,7 +881,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: no user-questions answerer accepted the request' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -923,7 +901,6 @@ describe('exit_plan_mode', () => { text: "Error: human interaction is unavailable while the calling agent is owned by another live agent; include the unresolved question or decision in the child agent's final result", }]) expect(ask).not.toHaveBeenCalled() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(child.session.snapshotEvents())).toBe(true) }) @@ -936,11 +913,9 @@ describe('exit_plan_mode', () => { expect(result.content).toEqual([{ type: 'text', text: 'Plan approved — plan mode exited; carry out the plan starting with your next step.' }]) // Boundary-applied, not a direct append: the fold stays plan until the // step's end, so the plan policy covers any remaining call of the SAME batch. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) expect(ctx.planMode.get(agent)).toEqual({ active: true, pending: false }) await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) expect(asked).toHaveLength(1) expect(asked[0]?.agent).toBe(agent) @@ -991,7 +966,6 @@ describe('exit_plan_mode', () => { question: 'Approve this plan and leave plan mode?', detail: plan, }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().find(event => event.type === 'tool/ptc-dispatch')?.data).toMatchObject({ name: EXIT_PLAN_MODE, arguments: { plan }, @@ -1007,13 +981,11 @@ describe('exit_plan_mode', () => { // Calls of the SAME assistant response were requested under the existing // plan-shaped header. Pending state shapes only the proposed next // assembly; the accepted boundary then commits the matching durable fold. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) const assembly = await ctx.systemPrompt.assemble({ agent }) expect(assembly.tools.some(tool => tool.name === EXIT_PLAN_MODE)).toBe(true) expect(assembly.sections.find(section => section.name === 'plan:policy')?.text).toBe('') await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) const afterExit = await ctx.systemPrompt.assemble({ agent }) expect(afterExit.tools).toEqual(assembly.tools) @@ -1025,7 +997,6 @@ describe('exit_plan_mode', () => { header(agent.session) await callExit(ctx, agent) await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(false) expect(noticeTexts(agent.session)).toEqual([]) }) @@ -1035,7 +1006,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; their feedback: consider the resume path' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1051,7 +1021,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; their feedback: add tests first' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1060,7 +1029,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; revise the plan and present it again.' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1069,7 +1037,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; their feedback: change the tests' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1084,7 +1051,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user chose to keep planning; revise the plan and present it again.' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1117,7 +1083,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: The user dismissed the plan review to speak instead; stay in plan mode, stop here, and wait for their message.' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1130,7 +1095,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: ask_user_question was aborted before the user answered' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1171,7 +1135,6 @@ describe('exit_plan_mode', () => { const result = await pending expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: the plan-mode service was reloaded while the plan was under review; present the plan again' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1181,7 +1144,6 @@ describe('exit_plan_mode', () => { const result = await callExit(ctx, agent) expect(result.isError).toBe(true) expect(result.content).toEqual([{ type: 'text', text: 'Error: review aborted' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldPlanMode(agent.session.snapshotEvents())).toBe(true) }) @@ -1233,7 +1195,6 @@ describe('HMR disposal', () => { expect(ctx.tools.get(EXIT_PLAN_MODE)).toBeUndefined() expect((await ctx.systemPrompt.assemble()).sections.map(section => section.name)).not.toContain('plan:policy') await boundary(ctx, agent, 'step-start') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(event => event.type === 'plan/mode')).toBe(false) }) }) diff --git a/packages/plan/plan-mode/tests/projection.spec.ts b/packages/plan/plan-mode/tests/projection.spec.ts index 1e15df94b4..f4d46d5b62 100644 --- a/packages/plan/plan-mode/tests/projection.spec.ts +++ b/packages/plan/plan-mode/tests/projection.spec.ts @@ -155,7 +155,6 @@ describe('plan projection unit', () => { // A second registry over the same log (the cold-read shape): no service // memory involved, the fold alone answers {active:false, pending:true}. const cold = await harness(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. for (const event of bench.session.snapshotEvents()) { if (event.type === 'command/run' || event.type === 'command/done' || event.type === 'plan/mode') { cold.session.append(event.type, event.data) diff --git a/packages/preset/agent-presets/tests/remote.spec.ts b/packages/preset/agent-presets/tests/remote.spec.ts index f02651d2f5..b1a685d019 100644 --- a/packages/preset/agent-presets/tests/remote.spec.ts +++ b/packages/preset/agent-presets/tests/remote.spec.ts @@ -98,7 +98,6 @@ async function agentOn(ctx: Context, id: string, presetId?: string): Promise - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. agent.session.snapshotEvents().findLast(event => event.type === 'agent-preset/selected')?.data describe('the roster a client reads', () => { diff --git a/packages/sandbox/sandbox-policy/tests/policy.spec.ts b/packages/sandbox/sandbox-policy/tests/policy.spec.ts index 543fbff319..608abeeade 100644 --- a/packages/sandbox/sandbox-policy/tests/policy.spec.ts +++ b/packages/sandbox/sandbox-policy/tests/policy.spec.ts @@ -207,7 +207,6 @@ describe('sandbox:policy request context', () => { it('reconstructs resumed policy from the session log and omits diagnostics without an agent', async () => { const active = session('sess-resume', '/projects/current') setSandboxMode(active, 'workspace-write') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumed = Session.create(active.id, active.snapshotEvents(), active.header) const ctx = await promptMounted({ mode: 'read-only' }) @@ -233,7 +232,6 @@ describe('the sandbox/mode session kit', () => { it('setSandboxMode appends exactly one sandbox/mode event per switch', () => { const session = Session.create(SessionId('sess-write')) setSandboxMode(session, 'danger-full-access') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const modeEvents = session.snapshotEvents().filter(e => e.type === 'sandbox/mode') expect(modeEvents).toHaveLength(1) expect(modeEvents[0]?.data).toEqual({ mode: 'danger-full-access' }) diff --git a/packages/schedule/schedule/tests/invariant.spec.ts b/packages/schedule/schedule/tests/invariant.spec.ts index 5600ea2635..f595bfa081 100644 --- a/packages/schedule/schedule/tests/invariant.spec.ts +++ b/packages/schedule/schedule/tests/invariant.spec.ts @@ -53,7 +53,6 @@ describe('Schedule package invariant', () => { const session = ctx.sessions.create(SessionId('schedule-invariant')) session.append('turn/start', { turn: 1 }) session.append('schedule/change', create('schedule-1')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(2) expect(() => session.append('schedule/change', { @@ -61,11 +60,9 @@ describe('Schedule package invariant', () => { operation: 'delete', id: ScheduleId('missing'), })).toThrow(InvariantError) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(2) session.append('schedule/change', { version: 1, operation: 'dispatch', id: ScheduleId('schedule-1') }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(3) await ctx.fiber.dispose() }) @@ -85,7 +82,6 @@ describe('Schedule package invariant', () => { id: ScheduleId('schedule-every'), acceptedAt: '2026-08-05T12:17:34.000Z', }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(2) await ctx.fiber.dispose() }) @@ -122,7 +118,6 @@ describe('Schedule package invariant', () => { }) const fiber = await ctx.plugin(scheduleInvariant) child.append('schedule/change', create('child')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) await fiber.dispose() await ctx.fiber.dispose() diff --git a/packages/schedule/schedule/tests/jsonl-restart.spec.ts b/packages/schedule/schedule/tests/jsonl-restart.spec.ts index 3a86c940d6..baf02e4702 100644 --- a/packages/schedule/schedule/tests/jsonl-restart.spec.ts +++ b/packages/schedule/schedule/tests/jsonl-restart.spec.ts @@ -107,7 +107,6 @@ describe('Schedule production JSONL restart', () => { ) pending.append('schedule/change', { version: 1, operation: 'create', schedule: pendingRecord }) const seed = await first.sessionPersistence.create(pending.header) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await seed.append(pending.snapshotEvents()) await seed.close() await disposeContext(first) @@ -144,7 +143,6 @@ describe('Schedule production JSONL restart', () => { await replayed.sessions.flush(replayHandle.agent.session) expect(replayAdapter.requests).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayHandle.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toHaveLength(1) const replayedStored = await readStored(replayed, sessionId) diff --git a/packages/schedule/schedule/tests/runtime.spec.ts b/packages/schedule/schedule/tests/runtime.spec.ts index a1daefbef8..f216babd7d 100644 --- a/packages/schedule/schedule/tests/runtime.spec.ts +++ b/packages/schedule/schedule/tests/runtime.spec.ts @@ -173,7 +173,6 @@ describe('Schedule timer and admission runtime', () => { await settle() expect(test.followed).toHaveLength(1) expect(test.controls.releaseCount).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().find(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toBeDefined() await runtime.dispose() @@ -224,7 +223,6 @@ describe('Schedule timer and admission runtime', () => { expect(test.followed).toEqual([]) expect(test.controls.whenIdleCount).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) runtime.requestDrive() @@ -297,14 +295,12 @@ describe('Schedule timer and admission runtime', () => { ].join('\n'), }]) expect(test.followed[0]?.source).toEqual({ kind: 'plugin', plugin: 'schedule' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const dispatches = test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch') expect(dispatches.map(event => event.data)).toEqual([ { version: 1, operation: 'dispatch', id: 'schedule-fast', acceptedAt: '2026-08-05T12:00:00.000Z' }, { version: 1, operation: 'dispatch', id: 'schedule-slow', acceptedAt: '2026-08-05T12:00:00.000Z' }, ]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldScheduleEvents(test.agent.session.snapshotEvents()).active).toEqual([ expect.objectContaining({ id: 'schedule-fast', scheduledAt: '2026-08-05T12:05:00.000Z' }), expect.objectContaining({ id: 'schedule-slow', scheduledAt: '2026-08-05T12:09:00.000Z' }), @@ -374,7 +370,6 @@ describe('Schedule timer and admission runtime', () => { expect(test.controls.releaseCount).toBe(1) expect(test.followed).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'delete' }) runtime.requestDrive() await settle() @@ -434,7 +429,6 @@ describe('Schedule runtime failure and teardown boundaries', () => { await settle() expect(test.controls.releaseCount).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toEqual([]) await runtime.dispose() @@ -466,7 +460,6 @@ describe('Schedule runtime failure and teardown boundaries', () => { expect(test.followed).toHaveLength(1) expect(test.controls.releaseCount).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toEqual([]) runtime.requestDrive() @@ -494,7 +487,6 @@ describe('Schedule runtime failure and teardown boundaries', () => { expect(test.followed).toHaveLength(1) expect(test.controls.releaseCount).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => ( event.type === 'schedule/change' && event.data.operation === 'dispatch' )).map(event => event.data)).toEqual([{ @@ -503,7 +495,6 @@ describe('Schedule runtime failure and teardown boundaries', () => { id: 'schedule-first', acceptedAt: '2026-08-05T12:00:00.000Z', }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldScheduleEvents(test.agent.session.snapshotEvents()).active).toEqual([ expect.objectContaining({ id: 'schedule-first', scheduledAt: '2026-08-05T12:05:00.000Z' }), expect.objectContaining({ id: 'schedule-second', scheduledAt: '2026-08-05T11:55:00.000Z' }), @@ -554,7 +545,6 @@ describe('Schedule runtime failure and teardown boundaries', () => { await settle() expect(test.controls.flushCount).toBe(1) expect(test.followed).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) await runtime.dispose() @@ -617,7 +607,6 @@ describe('Schedule runtime failure and teardown boundaries', () => { } await settle() expect(test.followed).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change' && event.data.operation === 'dispatch')).toEqual([]) }) diff --git a/packages/schedule/schedule/tests/tools.spec.ts b/packages/schedule/schedule/tests/tools.spec.ts index 467bc4f288..35e4dc1724 100644 --- a/packages/schedule/schedule/tests/tools.spec.ts +++ b/packages/schedule/schedule/tests/tools.spec.ts @@ -160,7 +160,6 @@ describe('Schedule tool protocol', () => { expect(value(await execute(test, 'schedule_create', { prompt: 'x', every_seconds: 299 }))) .toEqual({ code: 'frequency_too_high', message: 'every_seconds must be at least 300.' }) expect(test.flushes.count).toBe(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -234,7 +233,6 @@ describe('Schedule tool protocol', () => { expect.objectContaining({ id: 'schedule-1', kind: 'at' }), expect.objectContaining({ id: 'schedule-2', kind: 'at' }), ]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const changes = test.agent.session.snapshotEvents() .filter(event => event.type === 'schedule/change' && event.data.operation === 'create') expect(changes.map((change) => { @@ -303,7 +301,6 @@ describe('Schedule tool protocol', () => { message: 'The scheduled time must be strictly in the future.', }) expect(test.flushes.count).toBe(3) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -316,7 +313,6 @@ describe('Schedule tool protocol', () => { message: 'The scheduled time must be representable as a four-digit-year RFC 3339 UTC instant.', }) expect(test.flushes.count).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) const internal = await harness() @@ -443,7 +439,6 @@ describe('Schedule persistence failure boundaries', () => { error: { info: { name: 'AbortError', code: 'ABORTED' } }, }) expect(test.flushes.count).toBe(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -468,7 +463,6 @@ describe('Schedule persistence failure boundaries', () => { error: { info: { name: 'AbortError', code: 'ABORTED' } }, }) expect(test.flushes.count).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) }) @@ -492,7 +486,6 @@ describe('Schedule persistence failure boundaries', () => { error: { info: { name: 'AbortError', code: 'ABORTED' } }, }) expect(test.flushes.count).toBe(3) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(test.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')) .toHaveLength(1) expect(value(await execute(test, 'schedule_list', {}))) @@ -504,7 +497,6 @@ describe('Schedule persistence failure boundaries', () => { createTest.flushes.outcomes.push('reject') expect(value(await execute(createTest, 'schedule_create', { prompt: 'later', after_seconds: 1 }))) .toMatchObject({ code: 'persistence_uncertain', operation: 'create' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(createTest.agent.session.snapshotEvents().filter(event => event.type === 'schedule/change')).toEqual([]) const deleteTest = await harness() @@ -512,7 +504,6 @@ describe('Schedule persistence failure boundaries', () => { deleteTest.flushes.outcomes.push('reject') expect(value(await execute(deleteTest, 'schedule_delete', { id: 'schedule-1' }))) .toMatchObject({ code: 'persistence_uncertain', operation: 'delete', id: 'schedule-1' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(deleteTest.agent.session.snapshotEvents().at(-1)?.data).toMatchObject({ operation: 'create' }) }) diff --git a/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts b/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts index afb9125448..1d61e0ef3a 100644 --- a/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts +++ b/packages/session-query/session-log-export/tests/loader-composition.host.spec.ts @@ -65,7 +65,6 @@ describe('session-log-download real Loader composition', () => { }) const execution = await context.commands.execute(agent, '/export', [], new AbortController().signal) expect(execution?.result).toEqual({ kind: 'success', text: 'Session log download requested.' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual(['command/run', 'command/done']) expect(session.deriveMessages()).toEqual([]) }) diff --git a/packages/session-query/session-query/tests/session-query.spec.ts b/packages/session-query/session-query/tests/session-query.spec.ts index b1cfa90c28..ea89275ef3 100644 --- a/packages/session-query/session-query/tests/session-query.spec.ts +++ b/packages/session-query/session-query/tests/session-query.spec.ts @@ -1034,7 +1034,6 @@ describe('session-query exact reads', () => { }).toThrow() Object.assign(snapshot.session, { cwd: '/mutated' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.eventAt(SessionSeq(4)) expect(logged?.type === 'user/message' && logged.data.content).toHaveLength(1) expect(session.header.cwd).toBe('/work') @@ -1072,7 +1071,6 @@ describe('session-query exact reads', () => { (result.events[0]!.data as { content: unknown[] }).content = [] }).toThrow() expect(session.header.createdAt).not.toBe(-1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.eventAt(SessionSeq(1)) expect(logged?.type === 'user/message' && logged.data.content).toHaveLength(1) diff --git a/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts b/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts index 0ca1de7052..c127c2a127 100644 --- a/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts +++ b/packages/session-query/tool-session-query/tests/tool-session-query.spec.ts @@ -2006,7 +2006,6 @@ describe('trace and exact read rendering', () => { expect(text(result)).toContain('Replacement chain: 1') expect(text(result)).toContain('Events cited directly as sources: none') expect(text(result)).toContain('Direct derived events: 1') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(text(result)).toContain(new Date(session.snapshotEvents()[0]?.time ?? 0).toISOString()) }) diff --git a/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts b/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts index 31eb4d215f..76702f4c38 100644 --- a/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts +++ b/packages/session/session-log-deepseek/tests/feedback-composition.spec.ts @@ -84,7 +84,6 @@ it('uploads freeform feedback and message put/edit/delete through the unchanged recordFeedback(session, ' The session needs a clearer explanation. ') const created = await ctx.messageFeedback.put({ sessionId: session.id, messageId: assistant.id, rating: 'negative', note: 'Explain the result.', ifVersion: null }) if (!created.ok) throw new Error(created.error.code) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const initialPrefix = session.snapshotEvents() const request = async () => { const chunks = [] @@ -133,7 +132,6 @@ it('uploads freeform feedback and message put/edit/delete through the unchanged ] }) } await ctx.sessions.flush(session) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((await handle.read()).events).toEqual(session.snapshotEvents()) } finally { await handle.close() diff --git a/packages/session/session-log-deepseek/tests/upload.spec.ts b/packages/session/session-log-deepseek/tests/upload.spec.ts index dc4eb3f1c3..e92df32921 100644 --- a/packages/session/session-log-deepseek/tests/upload.spec.ts +++ b/packages/session/session-log-deepseek/tests/upload.spec.ts @@ -117,7 +117,6 @@ describe('incremental DeepSeek session-log upload', () => { turn: 1, step: 3, message: createSystemMessage('new head', 'fixture'), }, { surfaceOp: { op: 'replace', startSeq: head.seq, endSeq: head.seq }, sourceEventSeqs: [head.seq] }) const prepared = await ctx.deepseekLlmApiExtensions.prepare({ body: body(), signal: SIGNAL, sessionId: session.id }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(prepared.fields.dsh_session_log?.events).toEqual(session.snapshotEvents()) }) @@ -177,7 +176,6 @@ describe('incremental DeepSeek session-log upload', () => { expect(firstPayload?.events).toHaveLength(2) await first.accept() expect(SessionLogDeepSeek.acceptedThrough(session)).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().at(-1)?.data).toEqual({ sessionId: session.id, throughSeq: 1, @@ -199,7 +197,6 @@ describe('incremental DeepSeek session-log upload', () => { first.session.append('turn/start', { turn: 1 }) const prepared = await first.ctx.deepseekLlmApiExtensions.prepare({ body: body(), signal: SIGNAL, sessionId: first.session.id }) await prepared.accept() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = first.session.snapshotEvents() const resumed = await harness('parent', seed) @@ -364,7 +361,6 @@ describe('incremental DeepSeek session-log upload', () => { const { ctx, session } = await harness('direct-events') session.append('turn/start', { turn: 1 }) const prepared = await ctx.deepseekLlmApiExtensions.prepare({ body: {}, signal: SIGNAL, sessionId: session.id }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(prepared.fields.dsh_session_log?.events).toEqual(session.snapshotEvents()) }) diff --git a/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts b/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts index 814da947a6..9be35ec1bf 100644 --- a/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts +++ b/packages/session/session-persistence-jsonl/tests/multi-edge-publication.spec.ts @@ -176,7 +176,6 @@ function assertMigrated(result: Awaited>) { expect(session.firstLiveSeq).toBe(23) expect(session.isOwnSeq(SessionSeq(8))).toBe(false) expect(session.isOwnSeq(SessionSeq(9))).toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.ownEvents()).toEqual([ ...events.slice(9), expect.objectContaining({ type: 'session/end-seed', seq: 23, data: {} }), diff --git a/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts b/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts index 3c40b96b20..b8a7fad132 100644 --- a/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts +++ b/packages/session/session-persistence-jsonl/tests/v2-system-migration.spec.ts @@ -131,7 +131,6 @@ describe('V2 system prompts through current Session and JSONL persistence', () = const session = await restore(reader) expect(messages(session)).toEqual([human]) expect(session.surface.nodes).toEqual([8, 3]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.eventAt(SessionSeq(8))?.type).toBe('system/message') } finally { await reader.close() @@ -208,7 +207,6 @@ describe('V2 system prompts through current Session and JSONL persistence', () = expect(session.firstLiveSeq).toBe(15) expect(session.surface.nodes).toEqual([4, 8]) expect(messages(session)).toEqual([prompt('seed prompt'), { role: 'user', content: [{ type: 'text', text: 'summary' }] }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.ownEvents()).toEqual([prepared[14]]) } finally { await reader.close() @@ -229,9 +227,7 @@ describe('V2 system prompts through current Session and JSONL persistence', () = }, { surfaceOp: { op: 'replace', startSeq: SessionSeq(4), endSeq: SessionSeq(4) }, sourceEventSeqs: [SessionSeq(4)] }) session.append('step/end', { turn: 2, step: 1 }) session.append('turn/end', { turn: 2, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expected = session.snapshotEvents() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await writer.append(session.snapshotEvents(session.firstLiveSeq)) await writer.flush() } finally { @@ -290,7 +286,6 @@ describe('V2 system prompts through current Session and JSONL persistence', () = }, { surfaceOp: 'append' }) session.append('step/end', { turn: 1, step: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await writer.append(session.snapshotEvents()) await writer.flush() } finally { @@ -299,7 +294,6 @@ describe('V2 system prompts through current Session and JSONL persistence', () = const reopened = await mount() const reader = await reopened.sessionPersistence.open(id, 'read') try { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect((await reader.read()).events).toEqual(session.snapshotEvents()) const restored = await restore(reader) expect(restored.surface.nodes).toEqual([2, 3, 4]) diff --git a/packages/session/session-telemetry-otel/tests/otel.spec.ts b/packages/session/session-telemetry-otel/tests/otel.spec.ts index 35c2b89e11..b8919024bd 100644 --- a/packages/session/session-telemetry-otel/tests/otel.spec.ts +++ b/packages/session/session-telemetry-otel/tests/otel.spec.ts @@ -175,7 +175,6 @@ describe('OpenTelemetrySessionBackend wire', () => { const start = ledger.find(r => r.record.attributes?.some(a => a.key === 'event.type' && a.value.stringValue === 'turn/start')) expect(start).toBeDefined() expect(start?.record.severityNumber).toBe(9) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(BigInt(start!.record.timeUnixNano)).toBe(BigInt(session.snapshotEvents().find(event => event.type === 'turn/start')!.time) * 1_000_000n) expect(start?.record.attributes).toContainEqual({ key: 'session.format_version', @@ -241,7 +240,6 @@ describe('OpenTelemetrySessionBackend wire', () => { }, }) expect(eventTypes(captures)).not.toContain('manual') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) expect(ops).toHaveLength(0) }) @@ -495,7 +493,6 @@ describe('OpenTelemetrySessionBackend wire', () => { session.append('turn/start', { turn: 1 }) expect(captures).toEqual([]) recordFeedback(session, 'explicit report') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const submitted = session.snapshotEvents().map(event => event.type) await expect.poll(() => eventTypes(captures)).toEqual(submitted) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) @@ -521,7 +518,6 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { session.append('turn/start', { turn: 1 }) expect(captures).toEqual([]) recordFeedback(session, 'explicit report') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const expected = session.snapshotEvents().map(event => event.type) await expect.poll(() => eventTypes(captures)).toEqual(expected) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) @@ -539,7 +535,6 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { await ctx.plugin(SessionStore) const donor = Session.create(SessionId('stored-feedback')) recordFeedback(donor, 'old feedback is not a submission') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const restored = ctx.sessions.create(donor.id, { seed: donor.snapshotEvents(), meta: donor.header }) try { const first = await ctx.plugin(OpenTelemetrySessionBackend, { mode: SessionTelemetryMode.FEEDBACK_ONLY, exporter: { url } }) @@ -550,10 +545,8 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { session.append('request/header', { header: { config: { provider, model: 'm' } }, reason: 'change' }) } const child = ctx.sessions.fork(restored, undefined, SessionId('child')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const inherited = child.snapshotEvents().find(event => event.type === 'feedback/record')! ctx.emit('session/event', child, inherited) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const opened = ctx.sessions.create(SessionId('opened'), { seed: donor.snapshotEvents() }) ctx.emit('session/created', opened) await first.dispose() @@ -588,7 +581,6 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { expect(captures).toEqual([]) const put = await ctx.messageFeedback.put(request) if (!put.ok) throw new Error('live put failed') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await expect.poll(() => eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) const throughPut = session.seq await ctx.messageFeedback.put({ ...request, ifVersion: put.value.version }) @@ -596,10 +588,8 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { expect(session.seq).toBe(throughPut) const edit = await ctx.messageFeedback.put({ ...request, ifVersion: put.value.version, note: 'edited note' }) if (!edit.ok) throw new Error('live note failed') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await expect.poll(() => eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) await ctx.messageFeedback.delete({ sessionId: session.id, messageId: message.id, ifVersion: edit.value.version }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await expect.poll(() => eventTypes(captures)).toEqual(session.snapshotEvents().map(event => event.type)) const submitted = eventTypes(captures) const throughDelete = session.seq @@ -622,7 +612,6 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { const { ctx, fiber } = await boot(url) const session = Session.create(SessionId('cold-not-submitted')) const notify = async () => ctx.parallel('feedback/committed', { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. meta: session.header, events: session.snapshotEvents(), inheritedEventCount: session.inheritedEventCount, }) await notify() @@ -634,7 +623,6 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { child.append('feedback/message-delete', { sessionId: session.id, messageId: message.id }) await ctx.parallel('feedback/committed', { meta: { ...session.header, id: SessionId('inherited-cold'), parentSession: session.id, isSeeded: true }, - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. events: session.snapshotEvents(SessionLogOffset(0), SessionLogOffset(1)), inheritedEventCount: SessionLogOffset(1), }) await fiber.dispose() @@ -654,14 +642,12 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { parent.append('request/header', { header: { config: { provider: 'mock', model: 'm' } }, reason: 'initial' }) const message = createAssistantMessage({ content: [{ type: 'text', text: 'inherited answer' }], source: { provider: 'mock', model: 'm' } }) parent.append('assistant/message', { message, stream: [], turn: 1, step: 1 }, { surfaceOp: 'append' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = Session.create(SessionId('cold-child'), parent.snapshotEvents(), { ...parent.header, id: SessionId('cold-child'), parentSession: parent.id, isSeeded: true, }, parent.seq) recordFeedback(child, 'child-owned stored feedback') const handle = await ctx.sessionPersistence.create(child.header, { inheritedEventCount: child.inheritedEventCount }) try { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await handle.append(child.snapshotEvents()) } finally { await handle.close() @@ -709,7 +695,6 @@ describe('OpenTelemetrySessionBackend route and feedback', () => { session.append('assistant/message', { message, stream: [], turn: 1, step: 1 }, { surfaceOp: 'append' }) const handle = await ctx.sessionPersistence.create(session.header) try { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. await handle.append(session.snapshotEvents()) } finally { await handle.close() diff --git a/packages/session/session-telemetry/tests/redact.spec.ts b/packages/session/session-telemetry/tests/redact.spec.ts index 7ecdc624a0..b563d9c469 100644 --- a/packages/session/session-telemetry/tests/redact.spec.ts +++ b/packages/session/session-telemetry/tests/redact.spec.ts @@ -72,7 +72,6 @@ describe('session-telemetry/record waterfall', () => { session.append('user/message', createUserMessage({ content: [{ type: 'text', text: FIXTURE_SECRET }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[0]!.data as { content: { text: string }[] } expect(logged.content[0]!.text).toBe(FIXTURE_SECRET) }) @@ -125,7 +124,6 @@ describe('session-telemetry/record waterfall', () => { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' }, }), { surfaceOp: 'append' }) expect(backend.records).toHaveLength(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toHaveLength(1) }) }) diff --git a/packages/session/session-telemetry/tests/telemetry.spec.ts b/packages/session/session-telemetry/tests/telemetry.spec.ts index 8a5b50bbe7..ac9ffdcfc0 100644 --- a/packages/session/session-telemetry/tests/telemetry.spec.ts +++ b/packages/session/session-telemetry/tests/telemetry.spec.ts @@ -128,13 +128,11 @@ describe('SessionTelemetryCoordinator capture', () => { 'event.type': 'turn/start', 'event.seq': 0, }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(start.time).toBe(session.snapshotEvents()[0]!.time) expect(start.severity).toBe('info') expect(message.attributes['event.seq']).toBe(1) // Deep-copy isolation: mutating the handed-off body never reaches the log. ;(message.body as { content: { text: string }[] }).content[0]!.text = 'tampered' - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = session.snapshotEvents()[1] as SessionEvent<'user/message'> expect(logged.data.content[0]).toMatchObject({ text: 'hello' }) }) @@ -246,7 +244,6 @@ describe('SessionTelemetryCoordinator on-demand capture', () => { const session = liveSession(ctx, 'on-demand-prefix') appendTurn(session) appendAssistantMessage(session, 1, 1, ['first'], 100) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const firstBoundary = session.snapshotEvents()[2]!.seq appendAssistantMessage(session, 1, 2, ['second'], 200) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) @@ -302,7 +299,6 @@ describe('SessionTelemetryCoordinator on-demand capture', () => { }) const parent = liveSession(ctx, 'history-parent') appendTurn(parent) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = ctx.sessions.create(SessionId('history-child'), { seed: [...parent.snapshotEvents()] }) const boundary = child.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) child.append('turn/start', { turn: 2 }) @@ -402,7 +398,6 @@ describe('SessionTelemetryCoordinator adoption', () => { inject: ['sessions'], apply: (inner: Context) => void new SessionTelemetryCoordinator(inner, backend), }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const child = ctx.sessions.prepare(SessionId('seeded'), { seed: [...parent.snapshotEvents()], meta: {} }) child.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) ctx.sessions.enter(child) @@ -432,7 +427,6 @@ describe('SessionTelemetryCoordinator adoption', () => { // telemetry therefore receives a current-format object with the complete // migrated canonical seed. const resumed = ctx.sessions.prepare(SessionId('resumed'), { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: structuredClone(donor.snapshotEvents()) as SessionEvent[], meta: { version: SESSION_FORMAT_VERSION, @@ -466,9 +460,7 @@ describe('SessionTelemetryCoordinator adoption', () => { const parent = liveSession(ctx, 'stitch-parent') appendTurn(parent) const child = ctx.sessions.create(SessionId('stitch-child'), { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: [...parent.snapshotEvents()], - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. inheritedEventCount: SessionLogOffset(parent.snapshotEvents().length), meta: { parentSession: SessionId('stitch-parent'), isSeeded: true }, }) diff --git a/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts b/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts index ef37a9d131..429f1f5bd3 100644 --- a/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts +++ b/packages/session/session-title-all-prompts-llm/tests/provider.spec.ts @@ -53,7 +53,6 @@ describe('all-messages LLM title provider', () => { ctx.llm.registerAdapter(['current-route'], adapter) await ctx.plugin(providerPlugin, LLM_CONFIG) const session = ctx.sessions.create(SessionId('all-plugin'), { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. seed: seeded.snapshotEvents(), inheritedEventCount: seeded.seq, meta: { parentSession: seeded.id, isSeeded: true }, diff --git a/packages/session/session-title-llm/tests/llm.spec.ts b/packages/session/session-title-llm/tests/llm.spec.ts index bd9ce65dc4..240ddefa2b 100644 --- a/packages/session/session-title-llm/tests/llm.spec.ts +++ b/packages/session/session-title-llm/tests/llm.spec.ts @@ -126,7 +126,6 @@ describe('generateSessionTitleWithLlm', () => { const providerRequest = request(ctx) let requestWasLoggedAtDispatch = false const adapter = new RecordingAdapter(SCRIPT, () => { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. requestWasLoggedAtDispatch = providerRequest.session.snapshotEvents() .some(event => event.type === 'session/title-llm-request') }) @@ -163,7 +162,6 @@ describe('generateSessionTitleWithLlm', () => { const prompt = options.messages[0]?.content[0] expect(prompt?.type === 'text' && prompt.text).toContain('first prompt') expect(prompt?.type === 'text' && prompt.text).toContain('第二个问题') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(providerRequest.session.snapshotEvents().findLast(event => event.type === 'session/title-llm-request')?.data) .toEqual({ titleProvider: TITLE_PROVIDER, @@ -195,7 +193,6 @@ describe('generateSessionTitleWithLlm', () => { await expect(generateSessionTitleWithLlm(ctx, config, oversized, [selected], TITLE_PROVIDER)) .rejects.toThrow(/input.*bytes.*maxInputBytes/i) expect(adapter.requests).toEqual([]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(oversized.session.snapshotEvents().some(event => event.type === 'session/title-llm-request')).toBe(false) const withinLimit = resolveSessionTitleLlmConfig({ ...config, maxInputBytes: 1_000 }) @@ -264,7 +261,6 @@ describe('generateSessionTitleWithLlm', () => { providerRequest.messages, TITLE_PROVIDER, )).rejects.toMatchObject({ message, code }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(providerRequest.session.snapshotEvents().some(event => event.type === 'session/title-llm-request')).toBe(true) }) diff --git a/packages/session/session-title/tests/projection.spec.ts b/packages/session/session-title/tests/projection.spec.ts index 90f6a207bb..df2b5f1f91 100644 --- a/packages/session/session-title/tests/projection.spec.ts +++ b/packages/session/session-title/tests/projection.spec.ts @@ -17,7 +17,6 @@ async function harness(withTitleService: boolean): Promise<{ ctx: Context; sessi } function appendTitle(session: Session, title: string): SessionSeqType { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const messageSeq = session.snapshotEvents().find(event => event.type === 'user/message' && event.data.source.kind === 'user')?.seq ?? session.append('user/message', createUserMessage({ diff --git a/packages/session/session-title/tests/provider.spec.ts b/packages/session/session-title/tests/provider.spec.ts index 2708d51e2c..d9632ea3b6 100644 --- a/packages/session/session-title/tests/provider.spec.ts +++ b/packages/session/session-title/tests/provider.spec.ts @@ -66,9 +66,7 @@ describe('SessionTitleService Provider lifecycle', () => { const child = ctx.sessions.fork(parent, undefined, SessionId('title-child')) expect(ctx.sessionTitle.get(child)).toEqual(ctx.sessionTitle.get(parent)) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.snapshotEvents().find(event => event.type === 'session/title')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. .toEqual(parent.snapshotEvents().find(event => event.type === 'session/title')) const firstGenerate = vi.fn(async (request: SessionTitleProviderRequest) => ({ @@ -379,7 +377,6 @@ describe('SessionTitleService Provider lifecycle', () => { }))) await settle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'request/header')).toHaveLength(1) expect(requests).toHaveLength(2) expect(requests[1]).toMatchObject({ diff --git a/packages/session/session-title/tests/rename.spec.ts b/packages/session/session-title/tests/rename.spec.ts index 706511d2e3..9654c18edc 100644 --- a/packages/session/session-title/tests/rename.spec.ts +++ b/packages/session/session-title/tests/rename.spec.ts @@ -46,7 +46,6 @@ describe('SessionTitleService.rename', () => { messageSeqs: [], source: { kind: 'user' }, }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents().findLast(item => item.type === 'session/title') expect(event?.data).toEqual({ title: 'Hand picked name', @@ -54,7 +53,6 @@ describe('SessionTitleService.rename', () => { source: { kind: 'user' }, }) // foldSessionTitle round-trips the third source kind. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSessionTitle(session.snapshotEvents())?.source).toEqual({ kind: 'user' }) }) @@ -166,7 +164,6 @@ describe('SessionTitleService.rename', () => { await settle() // The released provider result must not append over the user title, and // the swallowed abort must not surface as an unhandled rejection. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const latest = session.snapshotEvents().findLast(item => item.type === 'session/title') expect(latest?.data).toMatchObject({ title: 'User wins', source: { kind: 'user' } }) }) diff --git a/packages/session/session-title/tests/service-contracts.spec.ts b/packages/session/session-title/tests/service-contracts.spec.ts index c0b13910fe..c6e0dc93c3 100644 --- a/packages/session/session-title/tests/service-contracts.spec.ts +++ b/packages/session/session-title/tests/service-contracts.spec.ts @@ -171,7 +171,6 @@ describe('SessionTitleService configuration and refresh boundaries', () => { }) const source = appendPrompt(seed, 'Create exactly one fallback title') seed.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const session = ctx.sessions.create(SessionId('fallback-concurrency'), { seed: seed.snapshotEvents() }) const results = await Promise.all([ @@ -180,9 +179,7 @@ describe('SessionTitleService configuration and refresh boundaries', () => { ]) expect(results[0]).toEqual(results[1]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'session/title')).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual([ 'turn/start', 'user/message', @@ -207,7 +204,6 @@ describe('SessionTitleService configuration and refresh boundaries', () => { }) await expect(refresh).resolves.toMatchObject({ title: 'Already accepted' }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'session/title')).toHaveLength(1) }) @@ -270,7 +266,6 @@ describe('SessionTitleService configuration and refresh boundaries', () => { await fiber.dispose() await settle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().some(event => event.type === 'session/title')).toBe(false) const inactiveError = await lifecycle.inactiveRefresh expect(inactiveError).toBeInstanceOf(Error) @@ -290,7 +285,6 @@ describe('SessionTitleService configuration and refresh boundaries', () => { await Promise.resolve() await fiber.dispose() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().some(event => event.type === 'session/title')).toBe(false) expect(warn).not.toHaveBeenCalled() }) diff --git a/packages/session/session-title/tests/session-title.spec.ts b/packages/session/session-title/tests/session-title.spec.ts index b5cbf31c3c..69edbb84ff 100644 --- a/packages/session/session-title/tests/session-title.spec.ts +++ b/packages/session/session-title/tests/session-title.spec.ts @@ -53,7 +53,6 @@ describe('SessionTitleService', () => { await settleTitles() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const titleEvent = session.snapshotEvents().findLast(event => event.type === 'session/title') expect(titleEvent).toMatchObject({ type: 'session/title', @@ -141,7 +140,6 @@ describe('SessionTitleService', () => { expect(first?.messageSeqs).toEqual([eligible.seq]) expect(ctx.sessionTitle.get(session)).toEqual(first) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'session/title')).toHaveLength(1) }) @@ -162,7 +160,6 @@ describe('SessionTitleService', () => { }, }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSessionTitle(seed.snapshotEvents())).toEqual({ title: 'Later', messageSeqs: [1, 4], @@ -172,7 +169,6 @@ describe('SessionTitleService', () => { model: { provider: 'mock', model: 'title-model' }, }, eventSeq: 1, - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. updatedAt: seed.snapshotEvents()[1]?.time, }) }) @@ -180,7 +176,6 @@ describe('SessionTitleService', () => { it('folds an empty or title-less log to undefined', () => { expect(foldSessionTitle([])).toBeUndefined() const empty = Session.create(SessionId('no-title')) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(foldSessionTitle(empty.snapshotEvents())).toBeUndefined() }) }) diff --git a/packages/shell/tool-bash/tests/integration.spec.ts b/packages/shell/tool-bash/tests/integration.spec.ts index d561bed780..cf2a4e1d2e 100644 --- a/packages/shell/tool-bash/tests/integration.spec.ts +++ b/packages/shell/tool-bash/tests/integration.spec.ts @@ -58,7 +58,6 @@ function waitForIdle(ctx: Context, agent: Agent): Promise { } function events(agent: Agent): readonly SessionEvent[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() } diff --git a/packages/shell/tool-bash/tests/tools.spec.ts b/packages/shell/tool-bash/tests/tools.spec.ts index b70b02691c..1dfbd27b81 100644 --- a/packages/shell/tool-bash/tests/tools.spec.ts +++ b/packages/shell/tool-bash/tests/tools.spec.ts @@ -631,7 +631,6 @@ describe('sandbox escalation through the generic task producer', () => { expect(prompted).not.toHaveBeenCalled() const malformed = sandboxAgent() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. ;(malformed.session.snapshotEvents() as unknown as Array<{ type: string; data: { mode: string }; seq: number }>).push({ type: 'sandbox/mode', data: { mode: 'unknown-mode' }, diff --git a/packages/skill/tool-skill/tests/tool-skill.spec.ts b/packages/skill/tool-skill/tests/tool-skill.spec.ts index 7fe649b1a8..603166de04 100644 --- a/packages/skill/tool-skill/tests/tool-skill.spec.ts +++ b/packages/skill/tool-skill/tests/tool-skill.spec.ts @@ -124,7 +124,6 @@ async function proposeStep( } function catalogMessages(session: Session): Extract[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return session.snapshotEvents().filter((event): event is Extract => event.type === 'user/message' && event.data.source.kind === 'skill-catalog') } diff --git a/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts b/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts index 04238d479b..35c8af4df3 100644 --- a/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts +++ b/packages/subagent/subagent-fork-in-process/tests/multi-subagent.spec.ts @@ -67,7 +67,6 @@ describe('multi-subagent coexistence (spawn + fork on one context)', () => { // Parent does one real turn first, so the fork has a completed turn to seed. parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent q1' }], source: { kind: 'user' } })) await parent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentPrefixLen = parent.session.snapshotEvents().length // Delegate to a fresh spawn child. @@ -89,7 +88,6 @@ describe('multi-subagent coexistence (spawn + fork on one context)', () => { expect(spawnChild.session.header.parentSession).toBe(parent.session.header.id) expect(forkChild.session.header.parentSession).toBe(parent.session.header.id) // The fork child inherited the parent's prefix; the spawn child did not. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(forkChild.session.snapshotEvents().slice(0, parentPrefixLen).some(e => e.type === 'user/message')).toBe(true) await spawnRun.dispose() @@ -98,14 +96,12 @@ describe('multi-subagent coexistence (spawn + fork on one context)', () => { // The parent is unaffected and keeps working after both delegations. parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent q2' }], source: { kind: 'user' } })) await parent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const lastParentMessage = parent.session.snapshotEvents().findLast(e => e.type === 'assistant/message') expect(lastParentMessage?.type === 'assistant/message' && text(lastParentMessage.data.message.content)).toBe('parent turn two') // The parent's OWN log never recorded the children's internal steps — its // only subagent-related entries would be tool/call+tool/result IF it had // used the tool, but here we called the service directly, so the parent log // is purely its own two turns. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().filter(e => e.type === 'turn/end')).toHaveLength(2) }) }) diff --git a/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts b/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts index 2e557bebb0..6f7080cd31 100644 --- a/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts +++ b/packages/subagent/subagent-fork-in-process/tests/subagent-fork-in-process.spec.ts @@ -84,7 +84,6 @@ describe('dsh-subagent-fork-in-process', () => { expect(text(result.output)).toBe('fresh child') const child = ctx.agents.get(run.id)! // Only the child's own turn — no seeded parent turns. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(e => e.type === 'turn/end')).toHaveLength(1) expect(child.session.header.isSeeded).toBe(false) expect(child.session.inheritedEventCount).toBe(0) @@ -97,7 +96,6 @@ describe('dsh-subagent-fork-in-process', () => { await parent.whenIdle() parent.followup(createUserMessage({ content: [{ type: 'text', text: 'q2' }], source: { kind: 'user' } })) await parent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentPrefixLen = parent.session.snapshotEvents().length const run = await start(ctx, 'fork', { prompt: [{ type: 'text', text: 'child q' }], parent }) @@ -105,9 +103,7 @@ describe('dsh-subagent-fork-in-process', () => { const child = ctx.agents.get(run.id)! expect(child.session.header.isSeeded).toBe(true) expect(child.session.inheritedEventCount).toBe(parentPrefixLen) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, parentPrefixLen).at(-1)?.type).toBe('turn/end') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, parentPrefixLen).filter(e => e.type === 'turn/end')).toHaveLength(2) await run.dispose() }) @@ -116,7 +112,6 @@ describe('dsh-subagent-fork-in-process', () => { const { ctx, parent } = await setup([textResponse('parent answer'), textResponse('child answer')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent question' }], source: { kind: 'user' } })) await parent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentPrefixLen = parent.session.snapshotEvents().length const run = await start(ctx, 'fork', { prompt: [{ type: 'text', text: 'child question' }], parent }) @@ -126,10 +121,8 @@ describe('dsh-subagent-fork-in-process', () => { const child = ctx.agents.get(run.id)! // The child's log STARTS with the parent's prefix (seeded), then its own turn. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().length).toBeGreaterThan(parentPrefixLen) // The seeded prefix carried the parent's user message. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seededUser = child.session.snapshotEvents().slice(0, parentPrefixLen).find(e => e.type === 'user/message') expect(seededUser).toBeDefined() // Lineage stamped. @@ -160,7 +153,6 @@ describe('dsh-subagent-fork-in-process', () => { const child = ctx.agents.get(run.id)! // The child's seed has exactly the ONE completed parent turn (the open one excluded). - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seedTurnEnds = child.session.snapshotEvents().filter(e => e.type === 'turn/end') // 1 from the seeded parent turn + 1 from the child's own completed turn. expect(seedTurnEnds.length).toBe(2) diff --git a/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts b/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts index 72d3426a8a..d3fc067a5a 100644 --- a/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/inheritance.spec.ts @@ -69,7 +69,6 @@ function spawnRequest(parent: Agent) { } function toolResultTexts(agent: Agent): string[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. return agent.session.snapshotEvents() .filter((event): event is SessionEvent<'tool/result'> => event.type === 'tool/result') .map(event => event.data.message.content @@ -87,7 +86,6 @@ describe('in-process policy inheritance', () => { setSandboxMode(parent.session, 'read-only') // No parent approval override: the child pin must not depend on one. expect(ctx.approval.overrideOf(parent.session)).toBeUndefined() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentLogLength = parent.session.snapshotEvents().length script.push( toolCallResponse('write', 'write', { file_path: blocked, content: 'escaped' }), @@ -102,7 +100,6 @@ describe('in-process policy inheritance', () => { await expect(readFile(blocked, 'utf8')).rejects.toMatchObject({ code: 'ENOENT' }) expect(toolResultTexts(child).join('\n')).toContain(READ_ONLY_DENIAL) expect(result.stopReason).toBe('completed') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, 2)).toMatchObject([ { type: 'sandbox/mode', seq: 0, data: { mode: 'read-only', source: 'delegation' } }, { type: 'approval/policy', seq: 1, data: { policy: 'never', source: 'delegation' } }, @@ -112,15 +109,12 @@ describe('in-process policy inheritance', () => { expect(child.session.inheritedEventCount).toBe(0) expect(ctx.sandboxPolicy.overrideOf(child.session)).toBe('read-only') expect(ctx.approval.overrideOf(child.session)).toBe('never') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const request = child.session.snapshotEvents().find( (event): event is SessionEvent<'request/header'> => event.type === 'request/header', ) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const systemNode = child.session.snapshotEvents().find( (event): event is SessionEvent<'system/message'> => event.type === 'system/message', ) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const runtimeContext = child.session.snapshotEvents().find( (event): event is SessionEvent<'user/message'> => event.type === 'user/message' && event.data.source.kind === 'plugin' @@ -148,7 +142,6 @@ describe('in-process policy inheritance', () => { .join('\n') expect(systemText).not.toContain('Approval prompts are disabled') expect(systemText).not.toContain('You are a delegated subagent') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents()).toHaveLength(parentLogLength) } finally { await run.dispose() @@ -160,7 +153,6 @@ describe('in-process policy inheritance', () => { const { ctx, parent } = await setupWalled(script) const blocked = join(workspace, 'fork-blocked.txt') setSandboxMode(parent.session, 'workspace-write') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = parent.session.snapshotEvents() setSandboxMode(parent.session, 'read-only') script.push( @@ -177,7 +169,6 @@ describe('in-process policy inheritance', () => { expect(child.session.inheritedEventCount).toBe(1) expect(child.session.firstLiveSeq).toBe(seed.length) // seq 1 is the constructor's end-seed marker. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(event => event.type === 'sandbox/mode')).toMatchObject([ { seq: 0, data: { mode: 'workspace-write' } }, { seq: 2, data: { mode: 'read-only', source: 'delegation' } }, @@ -224,9 +215,7 @@ describe('in-process policy inheritance', () => { await run.result const child = run.localAgent as Agent expect(await readFile(allowed, 'utf8')).toBe('fine') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().some(event => event.type === 'sandbox/mode')).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(event => event.type === 'approval/policy')).toMatchObject([ { seq: 0, data: { policy: 'never', source: 'delegation' } }, ]) @@ -266,11 +255,9 @@ describe('in-process policy inheritance', () => { expect(consulted).toBe(false) expect(toolResultTexts(child).join('\n')) .toContain('the user rejected escalating this operation to "workspace-write"') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const asked = child.session.snapshotEvents().find( (event): event is SessionEvent<'approval/asked'> => event.type === 'approval/asked', ) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const decided = child.session.snapshotEvents().find( (event): event is SessionEvent<'approval/decided'> => event.type === 'approval/decided', ) diff --git a/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts b/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts index e4ec2605b0..106b9e3eaa 100644 --- a/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/preset-inheritance.spec.ts @@ -85,7 +85,6 @@ describe('a child agent composed in-process', () => { const run = await startInProcessRun(spawnRequest(parent), {}) await run.result - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(run.localAgent?.session.snapshotEvents().some(event => event.type === 'system/message' && JSON.stringify(event.data.message.content).includes('section for preset_only'))).toBe(true) diff --git a/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts b/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts index 70398a2f9f..fa4bfdb22b 100644 --- a/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/structured.spec.ts @@ -196,7 +196,6 @@ describe('in-process structured output', () => { expect(result.structured).toEqual({ answer: 5 }) expect(sideEffectRan).toBe(false) const child = ctx.agents.get(run.id) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const sideEffectResult = child?.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.source.callId === 'c2') expect(sideEffectResult?.type === 'tool/result' && sideEffectResult.data.message.content[0].isError).toBe(true) @@ -241,7 +240,6 @@ describe('in-process structured output', () => { expect(result.stopReason).toBe('completed') // The child's log carries the isError tool/result for the invalid call. const child = ctx.agents.get(run.id)! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = child.session.snapshotEvents().filter(e => e.type === 'tool/result') expect(results.length).toBe(2) expect(results[0]!.data.message.content[0].isError).toBe(true) @@ -260,7 +258,6 @@ describe('in-process structured output', () => { // Exactly one model request and one caller-supplied user message: no nudge turn exists. expect(adapter.requests.length).toBe(1) const child = ctx.agents.get(run.id)! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(e => e.type === 'user/message' && e.data.source.kind !== 'plugin').length).toBe(1) await run.dispose() }) @@ -326,7 +323,6 @@ describe('in-process structured output', () => { expect(result.stopReason).toBe('error') // ...the logged tool result is the blocked isError with the feedback... const child = ctx.agents.get(run.id)! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const results = child.session.snapshotEvents().filter(e => e.type === 'tool/result') expect(results[0]!.data.message.content[0].isError).toBe(true) expect(JSON.stringify(results[0]!.data.message.content)).toContain('capture rejected by hook') @@ -372,7 +368,6 @@ describe('in-process structured output', () => { expect(result.structured).toBeUndefined() expect(result.stopReason).toBe('error') const child = ctx.agents.get(run.id) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const captureResult = child?.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.source.callId === 'c1') expect(captureResult?.type === 'tool/result' && captureResult.data.message.content[0].isError).toBe(true) @@ -445,7 +440,6 @@ describe('in-process structured output', () => { expect(result.stopReason).toBe('error') expect(adapter.requests).toHaveLength(2) const child = ctx.agents.get(run.id)! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const outer = child.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.source.callId === ToolCallId('c1')) expect(outer?.type === 'tool/result' && outer.data.message.content[0].isError).toBe(true) diff --git a/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts b/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts index e274ac7cb4..d11fa73b05 100644 --- a/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts +++ b/packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts @@ -150,7 +150,6 @@ describe('startInProcessRun', () => { let injected = false ctx.on('session/flush', (session) => { if (injected || session.header.parentSession === undefined) return - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const lastEnd = session.snapshotEvents().findLast(event => event.type === 'turn/end') if (lastEnd?.type !== 'turn/end' || lastEnd.data.reason.kind !== 'max-tokens') return injected = true @@ -165,7 +164,6 @@ describe('startInProcessRun', () => { const child = ctx.agents.get(run.id)! expect(injected).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().findLast(event => event.type === 'turn/end')) .toMatchObject({ data: { reason: { kind: 'max-tokens' } } }) expect(result.stopReason).toBe('max-tokens') @@ -201,7 +199,6 @@ describe('startInProcessRun', () => { const { ctx, parent } = await setup([textResponse('parent answer'), textResponse('child answer')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent question' }], source: { kind: 'user' } })) await parent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = parent.session.snapshotEvents() const run = await startInProcessRun(request(parent), { seed }) const result = await run.result @@ -209,13 +206,11 @@ describe('startInProcessRun', () => { const child = ctx.agents.get(run.id)! expect(child.session.header.isSeeded).toBe(true) expect(child.session.inheritedEventCount).toBe(seed.length) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().slice(0, seed.length)).toEqual(seed) // The seeded `system/message` stays surface node 0: the child renders the // same prompt, so its loop appends no second system node. const seededSystem = seed.find(event => event.type === 'system/message') expect(seededSystem).toBeDefined() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().filter(event => event.type === 'system/message')).toHaveLength(1) expect(child.session.surface.nodes[0]).toBe(seededSystem?.seq) expect(child.session.deriveMessages()[0]).toEqual(parent.session.deriveMessages()[0]) @@ -226,7 +221,6 @@ describe('startInProcessRun', () => { const { ctx, parent, adapter } = await setup([textResponse('parent answer'), textResponse('child answer')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent question' }], source: { kind: 'user' } })) await parent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const seed = parent.session.snapshotEvents() const seededSystem = seed.find(event => event.type === 'system/message') if (seededSystem === undefined) throw new Error('the parent log lacks a system node') @@ -235,7 +229,6 @@ describe('startInProcessRun', () => { const run = await startInProcessRun(request(parent), { seed }) await run.result const child = ctx.agents.get(run.id)! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const systemNodes = child.session.snapshotEvents().filter(event => event.type === 'system/message') expect(systemNodes.map(event => event.seq)).toEqual([seededSystem.seq, systemNodes[1]?.seq]) expect(systemNodes[1]?.surfaceOp).toEqual({ op: 'replace', startSeq: seededSystem.seq, endSeq: seededSystem.seq }) @@ -367,7 +360,6 @@ describe('startInProcessRun', () => { }) expect(adapter.requests[0]?.signal?.reason).toEqual({ kind: 'parent' }) const child = parent.ctx.agents.get(signalled.id) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const turnEnd = child?.session.snapshotEvents().findLast(event => event.type === 'turn/end') expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason).toEqual({ kind: 'aborted', reason: { kind: 'parent' } }) await signalled.dispose() diff --git a/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts b/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts index c031772384..3e764070f7 100644 --- a/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts +++ b/packages/subagent/subagent-spawn-in-process/tests/spawn-in-process.e2e.ts @@ -38,7 +38,6 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('spawn backend with-key smoke', ( // The parent's log records the subagent tool/call + its result (not the // child's internal steps). - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const events = parent.session.snapshotEvents() const subagentCalls = events.filter(e => e.type === 'tool/call' && e.data.name === 'subagent') expect(subagentCalls.length).toBeGreaterThan(0) diff --git a/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts b/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts index 957496e492..b678f5c263 100644 --- a/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts +++ b/packages/subagent/subagent-spawn-in-process/tests/subagent-spawn-in-process.spec.ts @@ -118,7 +118,6 @@ describe('dsh-subagent-spawn-in-process', () => { const { ctx, parent } = await setup([textResponse('parent turn'), textResponse('child sees nothing')]) parent.followup(createUserMessage({ content: [{ type: 'text', text: 'parent prompt' }], source: { kind: 'user' } })) await parent.whenIdle() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const parentEventCount = parent.session.snapshotEvents().length expect(parentEventCount).toBeGreaterThan(0) @@ -126,7 +125,6 @@ describe('dsh-subagent-spawn-in-process', () => { await run.result const child = ctx.agents.get(run.id)! // The child's first user/message is its OWN prompt, not the parent's history. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const firstUser = child.session.snapshotEvents().find(e => e.type === 'user/message') expect(firstUser).toBeDefined() await run.dispose() @@ -441,7 +439,6 @@ describe('dsh-subagent-spawn-in-process', () => { expect((childRequest.tools ?? []).map(t => t.name)).not.toContain('forbidden_tool') // …and the attempted call executed as UNKNOWN_TOOL (visible in the log). const child = ctx.agents.get(run.id)! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const toolResult = child.session.snapshotEvents().find(e => e.type === 'tool/result')! expect(JSON.stringify(toolResult.data)).toContain('unknown tool') await run.dispose() diff --git a/packages/subagent/subagent/tests/continuation.spec.ts b/packages/subagent/subagent/tests/continuation.spec.ts index ecaa72a347..f2bbd16ef5 100644 --- a/packages/subagent/subagent/tests/continuation.spec.ts +++ b/packages/subagent/subagent/tests/continuation.spec.ts @@ -258,7 +258,6 @@ describe('SubagentRuntime.startContinuable', () => { ctx.on('agent/inbox/inserted', ({ agent, message }) => { // Acceptance is the boundary `startContinuable` resolves at, so observe // the log state exactly there rather than after later microtasks. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. enqueued.push({ id: message.id, loggedYet: hasUserText(agent.session.snapshotEvents(), 'child task') }) }) @@ -451,7 +450,6 @@ describe('SubagentRuntime.startContinuable', () => { })).rejects.toBe(catalogFailure) await vi.waitFor(() => { expect(ctx.agents.get(childId)).toBeUndefined() }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().filter(event => event.type === 'subagent/catalog')).toEqual([]) }) @@ -531,7 +529,6 @@ describe('SubagentRuntime.startContinuable', () => { expect(found).toBeDefined() return found! }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const descriptor = child.session.snapshotEvents().find(event => event.type === 'subagent/descriptor') expect(descriptor?.data).toEqual({ @@ -567,7 +564,6 @@ describe('SubagentRuntime.startContinuable', () => { return found! }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().find(event => event.type === 'subagent/descriptor')?.data) .toEqual({ version: SUBAGENT_DESCRIPTOR_VERSION, @@ -1159,7 +1155,6 @@ describe('continuable durability and teardown', () => { child.followup(createUserMessage({ content: message('accepted during flush'), source: { kind: 'user' } })) await vi.waitFor(() => { expect(adapter.requests).toHaveLength(2) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(hasAssistantText(child.session.snapshotEvents(), 'late answer')).toBe(true) }) await child.whenIdle() @@ -1887,7 +1882,6 @@ describe('continuable review regressions', () => { const started = await ctx.subagents.startContinuable(startSpec(parent)) await vi.waitFor(() => { expect(adapter.requests).toHaveLength(1) }) const child = ctx.agents.get(started.childId)! - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = child.session.snapshotEvents().length const controller = new AbortController() @@ -2349,7 +2343,6 @@ describe('continuable review regressions', () => { /** Every settlement notice this agent received, in order, as flat text. */ function settlementNotices(agent: Agent): { sender: string; text: string; summary: string }[] { - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const logged = agent.session.snapshotEvents().flatMap(event => event.type === 'user/message' ? [event.data] : []) return [...logged, ...agent.inbox.nextStep, ...agent.inbox.nextTurn].flatMap((message) => { if (message.source.kind !== 'subagent-settled') return [] @@ -2412,7 +2405,6 @@ describe('continuable adjacent-Agent delivery', () => { await vi.waitFor(() => { expect(adapter.requests.filter(request => request.sessionId === parent.id)).toHaveLength(1) }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const delivered = parent.session.snapshotEvents().flatMap(event => event.type === 'user/message' && event.data.source.kind === 'agent-message' ? [event.data] : [])[0] expect(delivered?.id).toBe(messageId) @@ -2707,7 +2699,6 @@ describe('continuable settlement delivery', () => { // Turn 1 closed cleanly and no later turn opened, so the cancelled queue is // the only record that this epoch was cut short. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(hasUserText(child.session.snapshotEvents(), 'never runs')).toBe(false) await vi.waitFor(() => { expect(settlementNotices(parent)).toHaveLength(1) }) expect(settlementNotices(parent)[0]!.text).toBe( @@ -2876,9 +2867,7 @@ describe('continuable settlement delivery', () => { `Background subagent ${started.childId} was stopped before it finished.` + '\nIt left no closing message.', ) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().some(event => event.type === 'agent/inbox/spliced')).toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(false) expect(parent.status).toBe('idle') }) @@ -2895,7 +2884,6 @@ describe('continuable settlement delivery', () => { await drained expect(settlementNotices(parent)).toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(parent.session.snapshotEvents().some(event => event.type === 'turn/start')).toBe(false) }) diff --git a/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts b/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts index 2af3149716..f5921a3fab 100644 --- a/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts +++ b/packages/subagent/tool-subagent/tests/model-selection-settings.spec.ts @@ -347,7 +347,6 @@ describe('SubagentModelSelectionConfig', () => { const enabledSeed = Session.create(SessionId('enabled-seed')) enabledSeed.append('subagent/model-selection-policy', { allowedModels: ALLOWED_MODELS }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumedEnabled = await createAgent(ctx, 'resumed-enabled', { seed: enabledSeed.snapshotEvents() }) expect(selectable(ctx, resumedEnabled)).toBe(true) @@ -360,7 +359,6 @@ describe('SubagentModelSelectionConfig', () => { expect(selectable(ctx, resumedEmpty)).toBe(false) expect(subagentModelSelectionPolicy(ctx.sessionProjections, resumedEmpty.session)).toBeUndefined() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const resumedDisabled = await createAgent(ctx, 'resumed-disabled', { seed: oldSeed.snapshotEvents() }) expect(selectable(ctx, resumedDisabled)).toBe(false) expect(subagentModelSelectionPolicy(ctx.sessionProjections, resumedDisabled.session)).toBeUndefined() diff --git a/packages/terminal/terminal-bash/tests/index.spec.ts b/packages/terminal/terminal-bash/tests/index.spec.ts index 78216805a3..af707d8269 100644 --- a/packages/terminal/terminal-bash/tests/index.spec.ts +++ b/packages/terminal/terminal-bash/tests/index.spec.ts @@ -618,7 +618,6 @@ describe('terminal-bash plugin shape', () => { expect(() => { setSandboxMode(session, 'read-only') }).toThrow( 'cannot change sandbox mode from "danger-full-access" to "read-only" while persistent terminal sessions are open or being created; wait for creation to settle and close them first', ) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'sandbox/mode')).toHaveLength(1) const replacementFiber = await registerStubLocalBackend(ctx, () => stubLocalSession()) @@ -629,7 +628,6 @@ describe('terminal-bash plugin shape', () => { await ctx.terminals.kill(owner, created.sessionId) await ctx.terminals.kill(owner, second.sessionId) expect(() => { setSandboxMode(session, 'read-only') }).not.toThrow() - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'sandbox/mode')).toHaveLength(2) }) diff --git a/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts b/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts index f65454023a..f4235aad18 100644 --- a/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts +++ b/packages/test-support/agent-loop-testkit/tests/agent-loop-testkit.spec.ts @@ -96,7 +96,6 @@ describe('dsh-agent-loop-testkit', () => { { id: step.id, turn: 3 }, { id: turn.id, turn: 3 }, ]) - // 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', 'agent/inbox/spliced', diff --git a/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts b/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts index 31292f374c..f80929880d 100644 --- a/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts +++ b/packages/test-support/session-snapshot/tests/fixtures/workspace-context-compaction.ts @@ -19,13 +19,11 @@ export function apply(ctx: Context): void { || exec.arguments.file_path !== 'nested/task.txt') return downstream const agent = exec.agent const baseline = agent.session.surface.nodes - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. .map(seq => agent.session.snapshotEvents()[seq]) .find(event => event?.type === 'user/message' && event.data.source.kind === 'agent-instructions' && event.data.source.baseline === true) if (baseline === undefined) throw new Error('workspace baseline missing before snapshot compaction') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const openTurn = agent.session.snapshotEvents().findLast(event => event.type === 'turn/start') if (openTurn?.type !== 'turn/start') throw new Error('workspace snapshot compaction has no open turn') const compactionId = CompactionId('workspace-context-fixture') diff --git a/packages/todo/tool-todo/tests/integration.spec.ts b/packages/todo/tool-todo/tests/integration.spec.ts index cde02c8482..3384372a65 100644 --- a/packages/todo/tool-todo/tests/integration.spec.ts +++ b/packages/todo/tool-todo/tests/integration.spec.ts @@ -63,7 +63,6 @@ describe('todo_write tool through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'plan a two-step task' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const log = agent.session.snapshotEvents() expect(findEvent(log, 'tool/call').data.name).toBe('todo_write') expect(findEvent(log, 'tool/result').data.message.content[0].isError).toBe(false) @@ -92,10 +91,8 @@ describe('todo_write tool through the agent loop', () => { agent.followup(createUserMessage({ content: [{ type: 'text', text: 'plan then update' }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const todoEvents = agent.session.snapshotEvents().filter(e => e.type === 'todo/write') expect(todoEvents).toHaveLength(2) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(findEvent(agent.session.snapshotEvents(), 'todo/write', 'last').data.todos).toEqual([ { content: 'step one', status: 'completed' }, { content: 'step two', status: 'in_progress' }, diff --git a/packages/todo/tool-todo/tests/invariant.spec.ts b/packages/todo/tool-todo/tests/invariant.spec.ts index 0501982bdb..4b41b6a301 100644 --- a/packages/todo/tool-todo/tests/invariant.spec.ts +++ b/packages/todo/tool-todo/tests/invariant.spec.ts @@ -66,11 +66,9 @@ describe('todo snapshot invariants', () => { const session = ctx.sessions.create() session.append('turn/start', { turn: 1 }) session.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const before = session.snapshotEvents() expect(() => session.append('todo/write', { todos: [] })).toThrow(/outside any open turn/) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual(before) }) diff --git a/packages/todo/tool-todo/tests/loader-composition.spec.ts b/packages/todo/tool-todo/tests/loader-composition.spec.ts index 039ba4dc1f..b99909fa2a 100644 --- a/packages/todo/tool-todo/tests/loader-composition.spec.ts +++ b/packages/todo/tool-todo/tests/loader-composition.spec.ts @@ -112,7 +112,6 @@ describe('tool-todo real Loader composition through cordis.yml', () => { }) expect(result.isError).toBe(true) expect(resultText(result)).toContain('at most one task may be in_progress') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().some(e => e.type === 'todo/write')).toBe(false) }, 30_000) @@ -130,7 +129,6 @@ describe('tool-todo real Loader composition through cordis.yml', () => { agent: owner, }) expect(result.isError).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(owner.session.snapshotEvents().findLast(e => e.type === 'todo/write')?.data.todos).toEqual(PARALLEL_TODOS) }, 30_000) diff --git a/packages/todo/tool-todo/tests/tool-todo.spec.ts b/packages/todo/tool-todo/tests/tool-todo.spec.ts index b045128eac..a3d60f5b63 100644 --- a/packages/todo/tool-todo/tests/tool-todo.spec.ts +++ b/packages/todo/tool-todo/tests/tool-todo.spec.ts @@ -82,7 +82,6 @@ describe('dsh-tool-todo', () => { }) expect(text(result)).toContain('1 pending, 1 in progress, 0 completed') - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')! expect(event.data.todos).toEqual(todos) }) @@ -93,7 +92,6 @@ describe('dsh-tool-todo', () => { const result = await callTodo(ctx, { todos: [{ content: ' plan the work ', status: 'pending' }] }, { agent }) expect(result.isError).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')! expect(event.data.todos).toEqual([{ content: 'plan the work', status: 'pending' }]) }) @@ -107,7 +105,6 @@ describe('dsh-tool-todo', () => { { content: 'b', status: 'in_progress' }, ] }, { agent }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const current = agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos expect(current).toEqual([ { content: 'a', status: 'completed' }, @@ -142,7 +139,6 @@ describe('dsh-tool-todo', () => { todos, counts: { pending: 1, inProgress: 2, completed: 0 }, }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos).toEqual(todos) }) @@ -159,7 +155,6 @@ describe('dsh-tool-todo', () => { expect(result.isError).toBe(true) expect(text(result)).toContain('at most one task may be in_progress') // A rejected call must not reach the durable log. - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(agent.session.snapshotEvents().some(e => e.type === 'todo/write')).toBe(false) }) @@ -252,7 +247,6 @@ describe('todo/write event', () => { ] session.append('todo/write', { todos }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const event = session.snapshotEvents().findLast(e => e.type === 'todo/write')! expect(event.type).toBe('todo/write') expect(event.data.todos).toEqual(todos) @@ -274,7 +268,6 @@ describe('todo/write event', () => { { content: 'second', status: 'in_progress' }, ] }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const current = session.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos expect(current).toEqual([ { content: 'first', status: 'completed' }, @@ -300,13 +293,10 @@ describe('todo/write event', () => { original.append('turn/start', { turn: 1 }) original.append('todo/write', { todos: [{ content: 'only', status: 'completed' }] }) original.append('turn/end', { turn: 1, reason: { kind: 'completed' } }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const replayed = Session.create(SessionId('t4-replay'), original.snapshotEvents()) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayed.snapshotEvents().findLast(e => e.type === 'todo/write')!.data.todos) .toEqual([{ content: 'only', status: 'completed' }]) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(replayed.snapshotEvents(SessionLogOffset(0), original.seq)).toEqual(original.snapshotEvents()) expect(replayed.firstLiveSeq).toBe(original.seq) }) diff --git a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts index e752ae307c..e3404d6c12 100644 --- a/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts +++ b/packages/workflow/tool-workflow/tests/tool-workflow.spec.ts @@ -146,7 +146,6 @@ describe('dsh-tool-workflow', () => { engine.settleRun(runId, { value: 1, stopReason: 'completed', agentsStarted: 1 }) expect((await pending).isError).toBe(false) expect(engine.disposed).toBe(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => [event.type, event.data])).toEqual([ ['tool-workflow/run-start', { runId: 'run-1', name: 'audit' }], ['tool-workflow/agent-start', { @@ -167,11 +166,9 @@ describe('dsh-tool-workflow', () => { value: null, stopReason: 'completed', agentsStarted: 0, }) await vi.waitFor(() => { expect(engine.disposed).toBe(1) }) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual(['tool-workflow/run-start']) barrier.resolve(undefined) expect((await pending).isError).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().map(event => event.type)).toEqual([ 'tool-workflow/run-start', 'tool-workflow/run-end', ]) @@ -193,10 +190,8 @@ describe('dsh-tool-workflow', () => { engine.settleRun(secondId, { value: null, stopReason: 'error', error: 'child failed', agentsStarted: 1 }) expect((await first).isError).toBe(false) expect((await second).isError).toBe(true) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'tool-workflow/agent-start')) .toHaveLength(1) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents().filter(event => event.type === 'tool-workflow/run-end').map(event => event.data)) .toEqual([ { runId: 'run-1', stopReason: 'completed' }, @@ -213,7 +208,6 @@ describe('dsh-tool-workflow', () => { await vi.waitFor(() => { expect(engine.requests).toHaveLength(1) }) engine.settleRun(WorkflowRunId('run-1'), { value: null, stopReason: 'completed', agentsStarted: 0 }) expect((await pending).isError).toBe(false) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(session.snapshotEvents()).toEqual([]) }) @@ -246,7 +240,6 @@ describe('dsh-tool-workflow', () => { expect(engine.disposed).toBe(1) expect(warnings).toHaveLength(1) expect(warnings[0]).toContain(failedType) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. const types = session.snapshotEvents().map(event => event.type) const expectedPrefixes = { 'tool-workflow/run-start': [], diff --git a/scripts/oxlint-contract.spec.ts b/scripts/oxlint-contract.spec.ts index f358f71d9c..aab098c2aa 100644 --- a/scripts/oxlint-contract.spec.ts +++ b/scripts/oxlint-contract.spec.ts @@ -254,13 +254,30 @@ export const longProbe = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + } }, 90_000) - it('allows waived Session history reads while rejecting new reads and unrelated deprecations', async () => { + it('allows Session history reads only in tests or with existing-call waivers', async () => { const suffix = randomUUID() const configPath = await writeContractConfig(suffix) - const path = join(repositoryRoot, 'scripts', `oxlint-contract-${suffix}.ts`) + const testPaths = [ + `packages/core/session/tests/oxlint-contract-${suffix}.ts`, + `apps/cli/tests/oxlint-contract-${suffix}.ts`, + `scripts/oxlint-contract-${suffix}.spec.ts`, + ] + const productionPaths = [ + `packages/core/session/src/oxlint-contract-${suffix}.ts`, + `scripts/oxlint-contract-${suffix}.ts`, + ] + const paths = [...testPaths, ...productionPaths] + const reads = `import { Session, SessionSeq } from '@deepseek-ai/dsh-session' + +export function reads(session: Session): void { + session.snapshotEvents() + session.eventAt(SessionSeq(0)) + session.ownEvents() +} +` const existing = `import { Session, SessionSeq } from '@deepseek-ai/dsh-session' -export function existingReads(session: Session): void { +export function reads(session: Session): void { // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. session.snapshotEvents() // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. @@ -269,39 +286,50 @@ export function existingReads(session: Session): void { session.ownEvents() } ` - const added = ` + const unrelated = ` /** @deprecated Use the replacement API. */ function oldApi(): void {} -export function newReads(session: Session): void { - session.snapshotEvents() - session.eventAt(SessionSeq(0)) - session.ownEvents() +export function unrelatedRead(): void { oldApi() } ` try { - await writeFile(path, existing) - const args = ['--config', relative(repositoryRoot, configPath), '--format', 'unix', relative(repositoryRoot, path)] + await Promise.all([ + ...testPaths.map(path => writeFile(join(repositoryRoot, path), reads)), + ...productionPaths.map(path => writeFile(join(repositoryRoot, path), existing)), + ]) + const args = ['--config', relative(repositoryRoot, configPath), '--format', 'unix', ...paths] const allowed = runRepositoryOxlint(args) expect(allowed.error).toBeUndefined() expect(allowed.signal).toBeNull() expect(allowed.status, normalizedOutput(allowed)).toBe(0) - await writeFile(path, existing + added) + await Promise.all([ + ...testPaths.map(path => writeFile(join(repositoryRoot, path), reads + unrelated)), + ...productionPaths.map(path => writeFile(join(repositoryRoot, path), reads)), + ]) const rejected = runRepositoryOxlint(args) const output = normalizedOutput(rejected) expect(rejected.error).toBeUndefined() expect(rejected.signal).toBeNull() expect(rejected.status, output).toBe(1) - expect(output.match(/typescript\(no-deprecated\)/g), output).toHaveLength(4) + const diagnostics = output.split('\n').filter(line => line.includes('typescript(no-deprecated)')) + for (const path of testPaths) { + const reported = diagnostics.filter(line => line.startsWith(`${path}:`)) + expect(reported, output).toHaveLength(1) + expect(reported[0]).toContain('`oldApi` is deprecated') + } + for (const path of productionPaths) { + expect(diagnostics.filter(line => line.startsWith(`${path}:`)), output).toHaveLength(3) + } for (const method of ['snapshotEvents', 'eventAt', 'ownEvents', 'oldApi']) { expect(output).toContain(`\`${method}\` is deprecated`) } } finally { await Promise.all([ - rm(path, { force: true }), + ...paths.map(path => rm(join(repositoryRoot, path), { force: true })), rm(configPath, { force: true }), ]) } diff --git a/scripts/python-snapshot-workflow-order.spec.ts b/scripts/python-snapshot-workflow-order.spec.ts index 84f401c51b..964244f5da 100644 --- a/scripts/python-snapshot-workflow-order.spec.ts +++ b/scripts/python-snapshot-workflow-order.spec.ts @@ -82,7 +82,6 @@ describe('advanced Python snapshot workflow ordering', () => { const child = await entered.promise expect(child.id).toBe(run.id) expect(adapter.requests).toHaveLength(0) - // oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred. expect(child.session.snapshotEvents().some(event => event.type === 'subagent/descriptor')).toBe(false) parent.session.append('tool-workflow/agent-start', { runId: WorkflowRunId('run'), seq: 1, label: 'workflow-child', childId: child.id, From a61b0c5a567f03ce5c36e05387218cf9426e223b Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 13:16:09 +0800 Subject: [PATCH 05/20] test(lint): update the scoped deprecation fingerprint --- scripts/lint-rule-fingerprint.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lint-rule-fingerprint.spec.ts b/scripts/lint-rule-fingerprint.spec.ts index 4aecd9e985..5ad4653d4e 100644 --- a/scripts/lint-rule-fingerprint.spec.ts +++ b/scripts/lint-rule-fingerprint.spec.ts @@ -30,7 +30,7 @@ const profiles = { test: { count: 84, indexes: [0, 3, 4, 5], - sha256: '7800bf57f0d689a5623e4ad60315b10173158fb9cf988d3f005cb115f2245691', + sha256: '7e4e1ed0cfca06ac94eeb26081195b5f753e7527775d5f5b95ccfefac9d2b137', }, } as const satisfies Record From c433ac777681e576895f8fecee1069819bc0ae79 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 13:17:20 +0800 Subject: [PATCH 06/20] test(credentials): await reload commit notifications --- .../credentials-local/tests/watcher.spec.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/credentials/credentials-local/tests/watcher.spec.ts b/packages/credentials/credentials-local/tests/watcher.spec.ts index 314d425d1f..7a77e7952a 100644 --- a/packages/credentials/credentials-local/tests/watcher.spec.ts +++ b/packages/credentials/credentials-local/tests/watcher.spec.ts @@ -145,9 +145,15 @@ describe('watcher pipeline', () => { const dir = await tempDir() const path = join(dir, '.credentials.yaml') const ctx = await boot({ path, debounceMs: 5 }) + const firstUpdate = Promise.withResolvers() + const secondUpdate = Promise.withResolvers() let arm = true ctx.on('credentials/reference-updated', () => { - if (!arm) return + if (!arm) { + secondUpdate.resolve(undefined) + return + } + firstUpdate.resolve(undefined) throw Object.assign(new Error('forged relation'), { code: 'INVARIANT' }) }) const [instance] = await fakeInstances() @@ -156,16 +162,14 @@ describe('watcher pipeline', () => { instance!.watcher.emit('all', 'change', path) // The snapshot commits before the fan-out, so the value lands even though // the listener threw out of the refresh. - await vi.waitFor(async () => { - expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'first', source: 'file' }) - }) + await firstUpdate.promise + expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'first', source: 'file' }) arm = false await writeCredentials(path, 'version: 1\nrefs:\n DSH_CRED_PIPE: second\n') instance!.watcher.emit('all', 'change', path) - await vi.waitFor(async () => { - expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'second', source: 'file' }) - }) + await secondUpdate.promise + expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'second', source: 'file' }) }) it('quiesces the refresh pipeline before dispose completes', async () => { From 3744740b6256347606aa973eb24f304513f8ccf2 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 13:17:21 +0800 Subject: [PATCH 07/20] test(agent-team): await recovered mail acknowledgement --- .../experimental/agent-team/tests/persistence.spec.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/experimental/agent-team/tests/persistence.spec.ts b/packages/experimental/agent-team/tests/persistence.spec.ts index 5237fc0301..8ca2f1b1e9 100644 --- a/packages/experimental/agent-team/tests/persistence.spec.ts +++ b/packages/experimental/agent-team/tests/persistence.spec.ts @@ -311,12 +311,20 @@ for (const backend of backends) { await first.dispose() const second = await stack(backend, storageRoot, [textResponse('resumed teammate answer')]) + const delivered = Promise.withResolvers() + second.ctx.on('session/event', (session, event) => { + if (session.id === rootId && event.type === 'team/message/delivered' + && event.data.messageId === queued.messageId) delivered.resolve(undefined) + }) const rootHandle = await second.ctx.agents.resume({ resumeSessionId: rootId, agentOptions: { provider: 'mock', model: 'mock' }, }) + // The child is also absent before cold resume starts; delivery proves recovery reached the mailbox. + await delivered.promise + await settleMailbox(second.ctx) await vi.waitFor(() => { expect(second.ctx.agents.get(started.member.id)).toBeUndefined() }, { timeout: 5_000 }) - await vi.waitFor(() => { expect(durable(rootHandle.agent).pendingMessages).toEqual([]) }) + expect(durable(rootHandle.agent).pendingMessages).toEqual([]) const child = await storedEvents(second.ctx, started.member.id) const peerIds = child.flatMap(event => event.type === 'user/message' From 536754191668eed7764be95dc26fa344bb9c3ea0 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 13:40:51 +0800 Subject: [PATCH 08/20] Revert "test(agent-team): await recovered mail acknowledgement" This reverts commit 6e9b6abf38694f9e2ba4214e87356d90293f8dc8. --- .../experimental/agent-team/tests/persistence.spec.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/experimental/agent-team/tests/persistence.spec.ts b/packages/experimental/agent-team/tests/persistence.spec.ts index 8ca2f1b1e9..5237fc0301 100644 --- a/packages/experimental/agent-team/tests/persistence.spec.ts +++ b/packages/experimental/agent-team/tests/persistence.spec.ts @@ -311,20 +311,12 @@ for (const backend of backends) { await first.dispose() const second = await stack(backend, storageRoot, [textResponse('resumed teammate answer')]) - const delivered = Promise.withResolvers() - second.ctx.on('session/event', (session, event) => { - if (session.id === rootId && event.type === 'team/message/delivered' - && event.data.messageId === queued.messageId) delivered.resolve(undefined) - }) const rootHandle = await second.ctx.agents.resume({ resumeSessionId: rootId, agentOptions: { provider: 'mock', model: 'mock' }, }) - // The child is also absent before cold resume starts; delivery proves recovery reached the mailbox. - await delivered.promise - await settleMailbox(second.ctx) await vi.waitFor(() => { expect(second.ctx.agents.get(started.member.id)).toBeUndefined() }, { timeout: 5_000 }) - expect(durable(rootHandle.agent).pendingMessages).toEqual([]) + await vi.waitFor(() => { expect(durable(rootHandle.agent).pendingMessages).toEqual([]) }) const child = await storedEvents(second.ctx, started.member.id) const peerIds = child.flatMap(event => event.type === 'user/message' From ba1e73ddf56313b01bf22060b5293a6ee858d3ed Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 13:40:51 +0800 Subject: [PATCH 09/20] Revert "test(credentials): await reload commit notifications" This reverts commit 436ec72d4b744cbf3c1473680388d802940b6b9e. --- .../credentials-local/tests/watcher.spec.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/credentials/credentials-local/tests/watcher.spec.ts b/packages/credentials/credentials-local/tests/watcher.spec.ts index 7a77e7952a..314d425d1f 100644 --- a/packages/credentials/credentials-local/tests/watcher.spec.ts +++ b/packages/credentials/credentials-local/tests/watcher.spec.ts @@ -145,15 +145,9 @@ describe('watcher pipeline', () => { const dir = await tempDir() const path = join(dir, '.credentials.yaml') const ctx = await boot({ path, debounceMs: 5 }) - const firstUpdate = Promise.withResolvers() - const secondUpdate = Promise.withResolvers() let arm = true ctx.on('credentials/reference-updated', () => { - if (!arm) { - secondUpdate.resolve(undefined) - return - } - firstUpdate.resolve(undefined) + if (!arm) return throw Object.assign(new Error('forged relation'), { code: 'INVARIANT' }) }) const [instance] = await fakeInstances() @@ -162,14 +156,16 @@ describe('watcher pipeline', () => { instance!.watcher.emit('all', 'change', path) // The snapshot commits before the fan-out, so the value lands even though // the listener threw out of the refresh. - await firstUpdate.promise - expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'first', source: 'file' }) + await vi.waitFor(async () => { + expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'first', source: 'file' }) + }) arm = false await writeCredentials(path, 'version: 1\nrefs:\n DSH_CRED_PIPE: second\n') instance!.watcher.emit('all', 'change', path) - await secondUpdate.promise - expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'second', source: 'file' }) + await vi.waitFor(async () => { + expect(await ctx.credentials.resolve(KEY)).toEqual({ value: 'second', source: 'file' }) + }) }) it('quiesces the refresh pipeline before dispose completes', async () => { From 14da43d9ff2dc147fd15372a28f28c6df537905e Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 14:51:50 +0800 Subject: [PATCH 10/20] docs(session): link deprecated readers to the history policy --- ...09-09-deprecate-synchronous-session-event-reads.i18n.yaml | 4 ++-- .../2026-09-09-deprecate-synchronous-session-event-reads.md | 4 ++-- ...026-09-09-deprecate-synchronous-session-event-reads.zh.md | 4 ++-- docs/subsystems/session.i18n.yaml | 4 ++-- docs/subsystems/session.md | 3 +++ docs/subsystems/session.zh.md | 3 +++ packages/core/session/src/index.ts | 5 ++++- scripts/oxlint-contract.spec.ts | 5 ++++- 8 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml index 7a61bc540e..0cae49b087 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.i18n.yaml +++ b/.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: 1420227daf9a410e7db6a7a685bcf4433d7d7b0b -2026-09-09-deprecate-synchronous-session-event-reads.zh.md: e546337bcb15cf6682935729de37aab0c579d2a2 +2026-09-09-deprecate-synchronous-session-event-reads.md: a2a86b0d6500269531ca1088738aaa612076bfb4 +2026-09-09-deprecate-synchronous-session-event-reads.zh.md: 38468708a5323b6ebf94377f3cc8f2c86dc95dde diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md index 1420227daf..a2a86b0d65 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md +++ b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md @@ -16,7 +16,7 @@ All operations that synchronously read arbitrary positions or ranges of Session The three methods carry this rule in `@deprecated` JSDoc. This is an API-use decision; the current Session implementation still retains the complete event sequence in memory. -Repository test files may call these three readers to inspect emitted events and exercise Session history behavior. Their existing lint override allows `snapshotEvents`, `eventAt`, and `ownEvents`; all other deprecated names remain errors. This allowance also covers unrelated declarations with the same three names under the current linter. It does not apply to production source or ordinary repository scripts. +Repository test files, including `scripts/**/*.spec.{ts,tsx}`, may call these three readers to inspect emitted events and exercise Session history behavior. The test-file lint override allows `snapshotEvents`, `eventAt`, and `ownEvents`; all other deprecated names remain errors. This allowance also covers unrelated declarations with the same three names under the current linter. It does not apply to production source or non-test repository scripts. ### State needed after resume @@ -44,7 +44,7 @@ Fork and a small number of operations may genuinely need a complete historical s 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. -Existing calls outside test files carry line-scoped `typescript/no-deprecated` waivers with a migration-deferral reason. Remove a waiver when its call migrates; copying a waiver to a new production call violates this policy. The executable lint check accepts test reads and existing waived reads, rejects unwaived production reads, and rejects unrelated deprecated APIs in tests. Documentation checks verify the source-equivalent API declarations and bilingual records. +Calls outside test files carry line-scoped `typescript/no-deprecated` waivers that identify deferred migration or delegation between deprecated readers. Remove a waiver when its deprecated call is removed; copying a waiver to a new production call violates this policy. The executable lint check accepts test reads and existing waived reads, rejects unwaived production reads, and rejects unrelated deprecated APIs in tests. Documentation checks verify the source-equivalent API declarations and bilingual records. ## Related decisions diff --git a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md index e546337bcb..38468708a5 100644 --- a/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md +++ b/.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md @@ -16,7 +16,7 @@ Status: implemented 这三个方法通过 `@deprecated` JSDoc 声明该规则。这是 API 使用决策;当前 Session 实现仍在内存中保留完整事件序列。 -仓库测试文件可以调用这三个读取方法,以检查已发出的事件并验证 Session 历史行为。现有测试 lint override 允许 `snapshotEvents`、`eventAt` 和 `ownEvents`,其他弃用名称仍报错。在当前 linter 下,此豁免也覆盖使用这三个名称的其他声明。它不适用于生产源码或普通仓库脚本。 +仓库测试文件(包括 `scripts/**/*.spec.{ts,tsx}`)可以调用这三个读取方法,以检查已发出的事件并验证 Session 历史行为。测试文件 lint override 允许 `snapshotEvents`、`eventAt` 和 `ownEvents`,其他弃用名称仍报错。在当前 linter 下,此豁免也覆盖使用这三个名称的其他声明。它不适用于生产源码或非测试仓库脚本。 ### 恢复后需要的状态 @@ -44,7 +44,7 @@ fork 等少数操作可能确实需要完整历史序列或继承前缀。它们 新增领域行为必须使其事件数据与投影状态足以支持恢复后的执行。用户请求的历史仍可渐进式加载,确实需要完整历史的操作仍需设计存储读取路径。本决策不表示恢复或 fork 已经能够避免加载完整日志。 -测试文件之外的现有调用带有逐行的 `typescript/no-deprecated` 豁免,并说明暂缓迁移的理由。调用迁移后应删除豁免;将豁免复制到新增生产调用违反本策略。实际执行 lint 的检查允许测试读取和已有豁免调用,拒绝未豁免的生产读取,并拒绝测试中其他已弃用 API。文档检查校验与源码一致的 API 声明及双语记录。 +测试文件之外的调用带有逐行的 `typescript/no-deprecated` 豁免,说明暂缓迁移或已弃用读取方法之间的委托。删除已弃用调用时应一并删除其豁免;将豁免复制到新增生产调用违反本策略。实际执行 lint 的检查允许测试读取和已有豁免调用,拒绝未豁免的生产读取,并拒绝测试中其他已弃用 API。文档检查校验与源码一致的 API 声明及双语记录。 ## 相关决策 diff --git a/docs/subsystems/session.i18n.yaml b/docs/subsystems/session.i18n.yaml index 4d4f5bc022..c5dbb4944f 100644 --- a/docs/subsystems/session.i18n.yaml +++ b/docs/subsystems/session.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/subsystems/session.md -session.md: 077e90c66abe3b5f3f24d14c3fb25f356d887fa8 -session.zh.md: 6b9ceb85a276b0221c1e6ff474fbcdea77dff1fd +session.md: 187578ac720dd3b7facba768491bd5fb867aa397 +session.zh.md: 3dae495b695c4ddec220c83d872c4f2c5f9b13c3 diff --git a/docs/subsystems/session.md b/docs/subsystems/session.md index 077e90c66a..187578ac72 100644 --- a/docs/subsystems/session.md +++ b/docs/subsystems/session.md @@ -474,6 +474,7 @@ declare class Session { /** * Return the immutable event stored at one exact sequence number. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @param seq - event sequence number. * @returns the accepted event, or undefined when the log does not contain it. */ @@ -483,6 +484,7 @@ declare class Session { * A full current snapshot is reused until the next append; every previously * returned snapshot remains stable after later appends. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @param fromSeq - non-negative inclusive sequence number; defaults to the log start. * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end. * @returns a frozen array of the selected deeply frozen events. @@ -494,6 +496,7 @@ declare class Session { /** * Return this Session's events after its fork-inherited prefix. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @returns a fresh array containing child-owned events in log order. */ ownEvents(): readonly SessionEvent[]; diff --git a/docs/subsystems/session.zh.md b/docs/subsystems/session.zh.md index 6b9ceb85a2..3dae495b69 100644 --- a/docs/subsystems/session.zh.md +++ b/docs/subsystems/session.zh.md @@ -476,6 +476,7 @@ declare class Session { /** * Return the immutable event stored at one exact sequence number. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @param seq - event sequence number. * @returns the accepted event, or undefined when the log does not contain it. */ @@ -485,6 +486,7 @@ declare class Session { * A full current snapshot is reused until the next append; every previously * returned snapshot remains stable after later appends. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @param fromSeq - non-negative inclusive sequence number; defaults to the log start. * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end. * @returns a frozen array of the selected deeply frozen events. @@ -496,6 +498,7 @@ declare class Session { /** * Return this Session's events after its fork-inherited prefix. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @returns a fresh array containing child-owned events in log order. */ ownEvents(): readonly SessionEvent[]; diff --git a/packages/core/session/src/index.ts b/packages/core/session/src/index.ts index dbf124893e..12e9ed1918 100644 --- a/packages/core/session/src/index.ts +++ b/packages/core/session/src/index.ts @@ -616,6 +616,7 @@ export class Session { /** * Return the immutable event stored at one exact sequence number. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @param seq - event sequence number. * @returns the accepted event, or undefined when the log does not contain it. */ @@ -628,6 +629,7 @@ export class Session { * A full current snapshot is reused until the next append; every previously * returned snapshot remains stable after later appends. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @param fromSeq - non-negative inclusive sequence number; defaults to the log start. * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end. * @returns a frozen array of the selected deeply frozen events. @@ -646,10 +648,11 @@ export class Session { /** * Return this Session's events after its fork-inherited prefix. * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited. + * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md). * @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. + // oxlint-disable-next-line typescript/no-deprecated -- Deprecated reader delegates to the deprecated range read. return this.snapshotEvents(this.inheritedEventCount) } diff --git a/scripts/oxlint-contract.spec.ts b/scripts/oxlint-contract.spec.ts index aab098c2aa..1618907a1b 100644 --- a/scripts/oxlint-contract.spec.ts +++ b/scripts/oxlint-contract.spec.ts @@ -315,7 +315,7 @@ export function unrelatedRead(): void { expect(rejected.error).toBeUndefined() expect(rejected.signal).toBeNull() expect(rejected.status, output).toBe(1) - const diagnostics = output.split('\n').filter(line => line.includes('typescript(no-deprecated)')) + const diagnostics = output.split('\n').filter(line => /:\d+:\d+: `\w+` is deprecated\./.test(line)) for (const path of testPaths) { const reported = diagnostics.filter(line => line.startsWith(`${path}:`)) expect(reported, output).toHaveLength(1) @@ -327,6 +327,9 @@ export function unrelatedRead(): void { for (const method of ['snapshotEvents', 'eventAt', 'ownEvents', 'oldApi']) { expect(output).toContain(`\`${method}\` is deprecated`) } + expect(output).toContain( + 'See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).', + ) } finally { await Promise.all([ ...paths.map(path => rm(join(repositoryRoot, path), { force: true })), From 12ba944f549229b2c64555f9b8368843d5d139a3 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 9 Sep 2026 14:52:15 +0800 Subject: [PATCH 11/20] test(lint): cover example history reader exemptions --- scripts/oxlint-contract.spec.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/oxlint-contract.spec.ts b/scripts/oxlint-contract.spec.ts index 1618907a1b..8cb4098f17 100644 --- a/scripts/oxlint-contract.spec.ts +++ b/scripts/oxlint-contract.spec.ts @@ -257,9 +257,12 @@ export const longProbe = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + it('allows Session history reads only in tests or with existing-call waivers', async () => { const suffix = randomUUID() const configPath = await writeContractConfig(suffix) + const exampleRoot = `examples/oxlint-contract-${suffix}` + const examplePath = `${exampleRoot}/tests/reads.ts` const testPaths = [ `packages/core/session/tests/oxlint-contract-${suffix}.ts`, `apps/cli/tests/oxlint-contract-${suffix}.ts`, + examplePath, `scripts/oxlint-contract-${suffix}.spec.ts`, ] const productionPaths = [ @@ -296,6 +299,11 @@ export function unrelatedRead(): void { ` try { + await mkdir(join(repositoryRoot, exampleRoot, 'tests'), { recursive: true }) + await writeFile(join(repositoryRoot, exampleRoot, 'tsconfig.json'), JSON.stringify({ + extends: '../../tsconfig.base.json', + include: ['tests/**/*.ts'], + })) await Promise.all([ ...testPaths.map(path => writeFile(join(repositoryRoot, path), reads)), ...productionPaths.map(path => writeFile(join(repositoryRoot, path), existing)), @@ -332,7 +340,8 @@ export function unrelatedRead(): void { ) } finally { await Promise.all([ - ...paths.map(path => rm(join(repositoryRoot, path), { force: true })), + ...paths.filter(path => path !== examplePath).map(path => rm(join(repositoryRoot, path), { force: true })), + rm(join(repositoryRoot, exampleRoot), { recursive: true, force: true }), rm(configPath, { force: true }), ]) } From aaa02a39709893ea45ac220aa87194d8522325fe Mon Sep 17 00:00:00 2001 From: turtle1999 Date: Thu, 10 Sep 2026 17:27:52 +0800 Subject: [PATCH 12/20] fix(subprocess): settle a Linux scope left active with no processes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Range observation waited for an active transient scope to leave `active` before it would prove the managed range empty. systemd ends a scope only when it observes the populated-to-empty transition, so a payload killed before the manager placed it in the scope's cgroup leaves the unit active with an empty cgroup forever. `waitForExit()` then polled a unit that could not change, and `LocalSubprocessRuntime.disposeManagedProcesses()` awaited it without bound — teardown hung for any command cancelled immediately after spawn. Each state query now also reads the unit's process count. Once termination was requested and the launcher has exited, a still-active unit reporting no processes is the empty range: the launcher forked every process it will ever fork, so nothing later joins that cgroup. The owner stops the leftover unit so transient units cannot accumulate, and an unreported or `[not set]` count stays unknown and keeps waiting. The race needs a live user-systemd manager, so it stayed dormant wherever the native scope path was unavailable and surfaced once CI routed lanes onto the Blacksmith hosted image, whose runners accept that path. Ten repetitions of `local.spec.ts > disposal kills still-running processes and awaits their exit` in a Linux container with a user manager failed seven times before the change and passed ten times after it. --- ...28-subprocess-native-containment.i18n.yaml | 4 +- ...026-08-28-subprocess-native-containment.md | 4 +- ...-08-28-subprocess-native-containment.zh.md | 4 +- .../subprocess-local/README.i18n.yaml | 4 +- .../subprocess/subprocess-local/README.md | 2 +- .../subprocess/subprocess-local/README.zh.md | 2 +- .../subprocess-local/src/linux-scope.ts | 51 +++++++++++- .../tests/linux-scope.spec.ts | 77 +++++++++++++++++++ 8 files changed, 134 insertions(+), 14 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.i18n.yaml index f20124e9f9..2f925cf952 100644 --- a/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.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-08-28-subprocess-native-containment.md -2026-08-28-subprocess-native-containment.md: 8e1e12a8536f56c9ccb515cec4c07c730c8b9b84 -2026-08-28-subprocess-native-containment.zh.md: 83bc20a29d937bca9530ca107712e4a7b39d8d4d +2026-08-28-subprocess-native-containment.md: 7523f9d66e7a302f6ce9c77d93671a5b1303a5aa +2026-08-28-subprocess-native-containment.zh.md: 252a8e9fd058cf37a1a7a70f09394a6811d58580 diff --git a/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.md b/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.md index 8e1e12a853..7523f9d66e 100644 --- a/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.md +++ b/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.md @@ -22,7 +22,7 @@ The first eligible Linux ordinary or PTY call in one runtime deeply checks the e The parent creates one 0700 directory with a complete 0600 `launch-request.json` containing the final target cwd and environment. The private `DSH_SUBPROCESS_RUNNER` value locates that request while the runner starts from the provider cwd and a bootstrap-safe environment. `systemd-run --user --scope --quiet --collect --expand-environment=no` registers its process in the scope, then the one-shot bootstrap removes and validates the request, changes to the target cwd, restores the complete target environment, resolves a bare executable with the target PATH rules, clears `FD_CLOEXEC` on fd 0 through fd 2, and calls libc `execve()` with the original argv. The bootstrap becomes the target in place and preserves its inherited stdio; it does not remain as a supervisor. -Request consumption or a manager observation of a loaded unit establishes scope ownership. Unit absence before either fact remains unresolved while the direct launcher is running. If that launcher exits while the request remains unconsumed, the direct result rejects with startup failure unless its observed signal matches a termination requested while the launcher was running. A matching signal preserves the actual exit outcome for both ordinary and PTY launches; a recorded startup error always takes precedence. Range observation independently resolves an empty range when the launcher has exited and the unit is absent. The parent checks this unresolved interval every 50 milliseconds; after establishment, state queries back off exponentially to the existing 5-second systemctl bound. Each query reads both `LoadState` and `ActiveState`: loaded `inactive` or `failed`, or an established unit becoming `not-found`/`inactive` or otherwise collected away, proves the range empty. `active`, `activating`, `reloading`, and `deactivating` remain nonterminal. Unknown or malformed combinations and unreadable manager results reject `waitForExit()` instead of claiming quiescence. `terminate()` wakes a sleeping observer for an immediate recheck, and settlement cancels the losing backoff sleep. A strict sibling `startup-error.json` carries only request/bootstrap or target pre-exec failure, and the parent removes this spawn's private paths at observable lifecycle completion. +Request consumption or a manager observation of a loaded unit establishes scope ownership. Unit absence before either fact remains unresolved while the direct launcher is running. If that launcher exits while the request remains unconsumed, the direct result rejects with startup failure unless its observed signal matches a termination requested while the launcher was running. A matching signal preserves the actual exit outcome for both ordinary and PTY launches; a recorded startup error always takes precedence. Range observation independently resolves an empty range when the launcher has exited and the unit is absent. The parent checks this unresolved interval every 50 milliseconds; after establishment, state queries back off exponentially to the existing 5-second systemctl bound. Each query reads `LoadState`, `ActiveState`, and `TasksCurrent`: loaded `inactive` or `failed`, or an established unit becoming `not-found`/`inactive` or otherwise collected away, proves the range empty. `active`, `activating`, `reloading`, and `deactivating` remain nonterminal, except that once termination was requested and the launcher has exited, a still-active unit reporting no processes is an empty range: the manager ends a scope only on the populated-to-empty transition, so a payload killed before it entered the cgroup never triggers one. That conclusion stops the leftover unit so transient units cannot accumulate, while an unreported or `[not set]` process count stays unknown and keeps waiting. Unknown or malformed combinations and unreadable manager results reject `waitForExit()` instead of claiming quiescence. `terminate()` wakes a sleeping observer for an immediate recheck, and settlement cancels the losing backoff sleep. A strict sibling `startup-error.json` carries only request/bootstrap or target pre-exec failure, and the parent removes this spawn's private paths at observable lifecycle completion. The ordinary target result still comes from the same child process. The PTY path uses the same request and bootstrap without a resident runner, so the `node-pty` PID, process group, session leader, controlling terminal, foreground `inputWaiting`, `/dev/tty`, readiness, and direct terminal outcome retain their existing meanings while scope membership covers `setsid` and reparented descendants. @@ -54,7 +54,7 @@ This note owns the current native-containment mechanism. It partially updates th ## Verification -- Provider and Linux protocol suites pin synchronous NUL rejection before launch side effects, strict request/error decoding, target cwd and complete environment restoration, private-variable collision, symlink-sensitive PATH traversal with preserved argv, close-on-exec removal for inherited stdio, pre-exec error ownership, failed-deep-probe retry plus successful-deep-probe caching with per-call manager checks, the three scope-establishment states including requested versus unexpected exits with an unconsumed request, `LoadState`/`ActiveState` parsing, `reloading`, terminate wake-up with losing-delay cancellation, bounded established-scope backoff, and exactly-once PTY managed-owner cleanup. +- Provider and Linux protocol suites pin synchronous NUL rejection before launch side effects, strict request/error decoding, target cwd and complete environment restoration, private-variable collision, symlink-sensitive PATH traversal with preserved argv, close-on-exec removal for inherited stdio, pre-exec error ownership, failed-deep-probe retry plus successful-deep-probe caching with per-call manager checks, the three scope-establishment states including requested versus unexpected exits with an unconsumed request, `LoadState`/`ActiveState`/`TasksCurrent` parsing, releasing a leftover scope left active with no processes beside the live-client, unterminated, and unset-count cases, `reloading`, terminate wake-up with losing-delay cancellation, bounded established-scope backoff, and exactly-once PTY managed-owner cleanup. - Windows protocol and Win32 suites pin exactly two result branches, numeric-only target exits, ordinary-error start cancellation with raw parent-local reasons, the reduced `name`/`message`/`code`/`syscall`/`path` error record, the fixed `2`/`3`/`267` to `ENOENT`, `740` to `EACCES`, `5` to `EPERM`, `193` to `EFTYPE`, and remaining-code to `UNKNOWN` mapping, start delivery after runner spawn, empty-range settlement after pre-spawn failure, explicit ordinally sorted target environment blocks with `=C:` preservation and double-NUL termination, `uv_get_osfhandle()` carrier mapping and unsigned invalid-sentinel rejection, the null-device ignored-stdin carrier and piped non-ignored stdin, result-send and IPC-disconnect failures, direct-result latching before stdio settlement, active-process quiescence, and unique handle cleanup. - A keyless [`bash-startup-timeout`](../../../../snapshots/session/bash-startup-timeout/snapshot.yml) Session snapshot pins the model-facing timeout result. A Linux user-systemd fixture holds the launch request unconsumed at an input barrier and verifies cancellation plus range settlement. - Real Linux user-systemd tests run one ordinary and one `node-pty` `setsid`/reparent scenario through the production entry. They prove scope signalling and collection, bare executable lookup, escaped-descendant termination, range settlement, and unchanged PTY PID, session, controlling-terminal, foreground-input, `/dev/tty`, readiness, and startup-failure semantics. diff --git a/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.zh.md b/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.zh.md index 83bc20a29d..252a8e9fd0 100644 --- a/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-28-subprocess-native-containment.zh.md @@ -22,7 +22,7 @@ detached POSIX 进程组、Windows direct-parent 遍历与 PTY 后代扫描只 parent 创建一个 0700 目录,其中的完整 0600 `launch-request.json` 保存最终 target cwd 与环境。私有 `DSH_SUBPROCESS_RUNNER` 值负责定位该 request,runner 则从 provider cwd 与 bootstrap-safe 环境启动。`systemd-run --user --scope --quiet --collect --expand-environment=no` 先把自身进程注册到 scope,再由 one-shot bootstrap 删除并校验 request、切换到 target cwd、恢复完整 target 环境、按 target PATH 规则解析裸可执行文件、清除 fd 0 至 fd 2 的 `FD_CLOEXEC`,并使用原始 argv 调用 libc `execve()`。bootstrap 会原地成为 target 并保留继承的 stdio,不作为常驻 supervisor。 -request 被消费或 manager 已观察到 loaded unit 都能建立 scope ownership。在这两项事实出现前,只要 direct launcher 仍在运行,unit absence 就保持未决。如果 launcher 退出时 request 仍未消费,direct result 会以 startup failure reject,除非实际观察到的信号匹配 launcher 仍在运行时请求的终止信号。普通进程与 PTY 进程遇到匹配信号时都会保留实际退出结果;已记录的 startup error 始终优先。launcher 已退出且 unit 不存在时,range observation 会独立结算 empty-range wait。parent 每 50 毫秒检查一次这段未决区间;建立后,状态查询按指数增长间隔退避,最多达到既有的 5 秒 systemctl 上限。每次查询同时读取 `LoadState` 与 `ActiveState`:loaded `inactive` 或 `failed`,以及已经建立的 unit 变为 `not-found`/`inactive` 或被 collect 卸载,都能证明 range 为空。`active`、`activating`、`reloading` 与 `deactivating` 仍是非终态。未知或 malformed 组合以及不可读的 manager 结果会使 `waitForExit()` reject,而不是宣称完全停稳。`terminate()` 会唤醒正在休眠的 observer 立即复查,结算时会取消未胜出的退避 sleep。严格的同目录 `startup-error.json` 只承载 request/bootstrap 或 target pre-exec failure,parent 会在可观察生命周期完成时移除本次 spawn 的私有路径。 +request 被消费或 manager 已观察到 loaded unit 都能建立 scope ownership。在这两项事实出现前,只要 direct launcher 仍在运行,unit absence 就保持未决。如果 launcher 退出时 request 仍未消费,direct result 会以 startup failure reject,除非实际观察到的信号匹配 launcher 仍在运行时请求的终止信号。普通进程与 PTY 进程遇到匹配信号时都会保留实际退出结果;已记录的 startup error 始终优先。launcher 已退出且 unit 不存在时,range observation 会独立结算 empty-range wait。parent 每 50 毫秒检查一次这段未决区间;建立后,状态查询按指数增长间隔退避,最多达到既有的 5 秒 systemctl 上限。每次查询读取 `LoadState`、`ActiveState` 与 `TasksCurrent`:loaded `inactive` 或 `failed`,以及已经建立的 unit 变为 `not-found`/`inactive` 或被 collect 卸载,都能证明 range 为空。`active`、`activating`、`reloading` 与 `deactivating` 仍是非终态;例外是:一旦请求过终止且 launcher 已经退出,报告没有任何进程却仍 active 的 unit 就是空 range——manager 只在观测到 populated→empty 转变时才结束 scope,因此进入 cgroup 前就被杀死的 payload 永远不会触发该转变。该判定会 stop 掉这个遗留 unit,使 transient unit 不会累积;未上报或为 `[not set]` 的进程数仍视为未知并继续等待。未知或 malformed 组合以及不可读的 manager 结果会使 `waitForExit()` reject,而不是宣称完全停稳。`terminate()` 会唤醒正在休眠的 observer 立即复查,结算时会取消未胜出的退避 sleep。严格的同目录 `startup-error.json` 只承载 request/bootstrap 或 target pre-exec failure,parent 会在可观察生命周期完成时移除本次 spawn 的私有路径。 普通 target result 仍来自同一个 child process。PTY 路径复用同一 request 与 bootstrap,但不增加常驻 runner,因此 `node-pty` PID、进程组、session leader、控制终端、前台 `inputWaiting`、`/dev/tty`、readiness 与 direct terminal outcome 保留既有含义,同时 scope membership 覆盖 `setsid` 与 reparent 后代。 @@ -54,7 +54,7 @@ selector 是 per-spawn locator 或 sentinel,不是凭据或持久格式。Linu ## Verification -- provider 与 Linux 协议测试套件固定同步 NUL 拒绝发生在启动副作用之前、严格 request/error 解码、target cwd 与完整环境恢复、私有变量碰撞、保留 argv 且对 symlink 敏感的 PATH 遍历、为继承 stdio 清除 close-on-exec、pre-exec error ownership、失败深度 probe 重试与成功深度 probe 缓存及逐调用 manager 检查、三种 scope 建立状态(包括 request 未消费时的请求终止与意外退出)、`LoadState`/`ActiveState` 解析、`reloading`、带未胜出 delay 取消的 terminate wake-up、建立后有上限的退避,以及 PTY managed-owner 恰好一次 cleanup。 +- provider 与 Linux 协议测试套件固定同步 NUL 拒绝发生在启动副作用之前、严格 request/error 解码、target cwd 与完整环境恢复、私有变量碰撞、保留 argv 且对 symlink 敏感的 PATH 遍历、为继承 stdio 清除 close-on-exec、pre-exec error ownership、失败深度 probe 重试与成功深度 probe 缓存及逐调用 manager 检查、三种 scope 建立状态(包括 request 未消费时的请求终止与意外退出)、`LoadState`/`ActiveState`/`TasksCurrent` 解析、释放被留在 active 且没有任何进程的遗留 scope(连同 client 仍存活、未请求终止与进程数未上报三种情形)、`reloading`、带未胜出 delay 取消的 terminate wake-up、建立后有上限的退避,以及 PTY managed-owner 恰好一次 cleanup。 - Windows 协议与 Win32 测试套件固定恰好两个 result 分支、只含数字的 target exit、使用普通 error 的 start cancellation 与 parent 原样保留的本地 reason、缩减到 `name`/`message`/`code`/`syscall`/`path` 的 error record、固定的 `2`/`3`/`267` 到 `ENOENT`、`740` 到 `EACCES`、`5` 到 `EPERM`、`193` 到 `EFTYPE` 及其余 code 到 `UNKNOWN` 的映射、runner spawn 后才发送 start、spawn 前 failure 的 empty-range settlement、按序数显式排序的 target 环境块及 `=C:` 保留和双 NUL 结尾、`uv_get_osfhandle()` carrier 映射与 unsigned invalid sentinel 拒绝、null-device ignored-stdin carrier 与非 ignore stdin pipe、result-send 与 IPC-disconnect failure、stdio settlement 前的 direct-result 锁存、active-process 完全停稳,以及唯一 handle cleanup。 - 无需密钥的 [`bash-startup-timeout`](../../../../snapshots/session/bash-startup-timeout/snapshot.yml) Session 快照固定模型可见的超时结果。Linux user-systemd fixture 通过输入屏障保持启动请求未消费,并验证取消与 range settlement。 - 真实 Linux user-systemd 测试会分别通过生产入口运行一条普通命令与一条 `node-pty` `setsid`/reparent 场景。它们证明 scope signalling 与 collection、裸可执行文件查找、逃逸后代终止、range settlement,以及不变的 PTY PID、session、控制终端、前台输入、`/dev/tty`、readiness 与 startup-failure 语义。 diff --git a/packages/subprocess/subprocess-local/README.i18n.yaml b/packages/subprocess/subprocess-local/README.i18n.yaml index 5cc4ae4c49..f4573b93cc 100644 --- a/packages/subprocess/subprocess-local/README.i18n.yaml +++ b/packages/subprocess/subprocess-local/README.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 packages/subprocess/subprocess-local/README.md -README.md: 03856c32e6308a1d075ff41b879da144cc772843 -README.zh.md: d1fb5881396963bf5a8d7cb4f44cabae3d1e5a24 +README.md: 3222d7bf9b9b498baf939212ebbf7da9bf599d76 +README.zh.md: e8d2ddc9398ffe186260a1b4ed3a0cd116ff8afb diff --git a/packages/subprocess/subprocess-local/README.md b/packages/subprocess/subprocess-local/README.md index 03856c32e6..3222d7bf9b 100644 --- a/packages/subprocess/subprocess-local/README.md +++ b/packages/subprocess/subprocess-local/README.md @@ -52,7 +52,7 @@ Collect mode keeps the last `maxBytes` of a stream in memory — errors and fina Normal disposal terminates every running managed range and terminal session and awaits quiescence. During a JavaScript-observable host exit — direct `process.exit()`, default uncaught exceptions, default unhandled rejections — synchronous finalization asks a Linux scope to kill its members, kills each Windows runner so its sole Job handle closes, and uses the existing PGID, `taskkill`, or captured-identity operation for fallbacks. It creates no promises or timers and does not claim quiescence. The same exit removes the private per-process spill directory when it holds no completed spill file; completed spill files remain as full-output recovery artifacts until an external cleanup. Unhandled `SIGTERM`/`SIGINT`/`SIGHUP`, `SIGKILL`, fatal OOM, native crashes, and power loss need an external supervisor. -Linux ordinary and terminal cancellation preserves the observed termination signal even before the bootstrap consumes its launch request. An unconsumed request still reports startup failure when no matching termination was requested; a recorded pre-exec error always takes precedence. `waitForExit()` independently proves the scope empty. +Linux ordinary and terminal cancellation preserves the observed termination signal even before the bootstrap consumes its launch request. An unconsumed request still reports startup failure when no matching termination was requested; a recorded pre-exec error always takes precedence. `waitForExit()` independently proves the scope empty, including a scope the manager leaves active with no processes after a payload dies before it enters that scope's cgroup. ### What can go wrong diff --git a/packages/subprocess/subprocess-local/README.zh.md b/packages/subprocess/subprocess-local/README.zh.md index d1fb588139..e8d2ddc939 100644 --- a/packages/subprocess/subprocess-local/README.zh.md +++ b/packages/subprocess/subprocess-local/README.zh.md @@ -52,7 +52,7 @@ kind: "package-reference" 正常 dispose 会终止每个仍在运行的受管范围与终端会话并等待其完全停稳。在 JavaScript 可观察的宿主退出期间——直接 `process.exit()`、默认未捕获异常、默认未处理 rejection——同步最终清理会请求 Linux scope 终止其成员,同步终止每个 Windows runner 以关闭其唯一 Job handle,并为 fallback 使用既有 PGID、`taskkill` 或已捕获身份操作。它不创建 Promise 或定时器,也不声称已经完全停稳。同一退出阶段会删除未持有任何已完成 spill 文件的每进程私有 spill 目录;已完成的 spill 文件作为完整输出恢复产物保留,直到外部机制清理。未处理的 `SIGTERM`/`SIGINT`/`SIGHUP`、`SIGKILL`、fatal OOM、native crash 与断电需要外部 supervisor。 -Linux 普通进程和终端进程即使在 bootstrap 消费启动请求前被取消,也会保留实际观察到的终止信号。如果没有请求对应的终止信号,未消费的请求仍会报启动失败;已记录的 pre-exec 错误始终优先。`waitForExit()` 独立证明 scope 已为空。 +Linux 普通进程和终端进程即使在 bootstrap 消费启动请求前被取消,也会保留实际观察到的终止信号。如果没有请求对应的终止信号,未消费的请求仍会报启动失败;已记录的 pre-exec 错误始终优先。`waitForExit()` 独立证明 scope 已为空,其中也包括 payload 在进入该 scope 的 cgroup 前就被杀死、manager 因此让它保持 active 却没有任何进程的 scope。 ### 可能出错的地方 diff --git a/packages/subprocess/subprocess-local/src/linux-scope.ts b/packages/subprocess/subprocess-local/src/linux-scope.ts index d5f28b5fb2..c018dc01a3 100644 --- a/packages/subprocess/subprocess-local/src/linux-scope.ts +++ b/packages/subprocess/subprocess-local/src/linux-scope.ts @@ -178,6 +178,7 @@ class LinuxScopeStartup { class SystemdScopeOwner implements BoundProcessOwner { private establishment: 'pending' | 'established' = 'pending' private stopped = false + private terminationRequested = false private observation: Promise | undefined private killFailure: Error | undefined private wakeGeneration = 0 @@ -195,6 +196,7 @@ class SystemdScopeOwner implements BoundProcessOwner { signal(signal: 'SIGTERM' | 'SIGKILL'): void { if (this.stopped) return + this.terminationRequested = true if (this.direct.running()) this.startup.terminationSignals.add(signal) this.observeRequestConsumption() const directFallbackRequired = this.establishment === 'pending' @@ -256,7 +258,32 @@ class SystemdScopeOwner implements BoundProcessOwner { return true } - private parseUnitState(stdout: string): { loadState: string; activeState: string } { + /** + * Prove an active unit with no processes is the empty managed range rather + * than a launch still placing its payload. systemd ends a scope only on the + * populated-to-empty transition, so a payload killed before it entered the + * cgroup leaves the unit active forever. Requested termination plus a + * departed client makes that leftover conclusive: the client forked every + * process it will ever fork. + */ + private emptyRange(tasksCurrent: number | undefined): boolean { + return this.terminationRequested && tasksCurrent === 0 && !this.direct.running() + } + + /** Release a leftover empty scope so the transient unit is collected and cannot accumulate. */ + private releaseEmptyRange(): void { + try { + this.runSync(this.systemctl, ['--user', 'stop', this.unit], { + env: managerEnvironment(), + stdio: 'ignore', + timeout: SYSTEMCTL_TIMEOUT_MS, + }) + } catch { + // The range is already empty; a failed cleanup leaves only the transient unit. + } + } + + private parseUnitState(stdout: string): { loadState: string; activeState: string; tasksCurrent: number | undefined } { const values = new Map() for (const line of stdout.split(/\r?\n/u)) { if (line === '') continue @@ -272,10 +299,21 @@ class SystemdScopeOwner implements BoundProcessOwner { } const loadState = values.get('LoadState') const activeState = values.get('ActiveState') - if (values.size !== 2 || loadState === undefined || activeState === undefined) { + // The manager prints this sentinel for a property the unit does not carry. + const reportedTasks = values.get('TasksCurrent') + const tasksCurrent = reportedTasks === '[not set]' ? undefined : reportedTasks + if (values.size !== (reportedTasks === undefined ? 2 : 3) + || loadState === undefined || activeState === undefined) { throw new Error(`systemctl returned incomplete state for ${this.unit}: ${JSON.stringify(stdout.trim())}`) } - return { loadState, activeState } + if (tasksCurrent !== undefined && !/^\d+$/u.test(tasksCurrent)) { + throw new Error(`systemctl returned a non-numeric TasksCurrent for ${this.unit}: ${JSON.stringify(tasksCurrent)}`) + } + return { + loadState, + activeState, + tasksCurrent: tasksCurrent === undefined ? undefined : Number(tasksCurrent), + } } private async rangeActive(): Promise { @@ -286,10 +324,11 @@ class SystemdScopeOwner implements BoundProcessOwner { this.unit, '--property=LoadState', '--property=ActiveState', + '--property=TasksCurrent', ]) const output = `${result.stdout}\n${result.stderr}` if (result.status === 0) { - const { loadState, activeState } = this.parseUnitState(result.stdout) + const { loadState, activeState, tasksCurrent } = this.parseUnitState(result.stdout) if (loadState === 'not-found' && activeState === 'inactive') return this.absentUnit() if (loadState !== 'loaded') { throw new Error( @@ -302,6 +341,10 @@ class SystemdScopeOwner implements BoundProcessOwner { throw new Error(`systemctl returned unknown ActiveState for ${this.unit}: ${JSON.stringify(activeState)}`) } if (this.killFailure !== undefined) throw this.killFailure + if (this.emptyRange(tasksCurrent)) { + this.releaseEmptyRange() + return false + } return true } if (!MISSING_UNIT.test(output)) { diff --git a/packages/subprocess/subprocess-local/tests/linux-scope.spec.ts b/packages/subprocess/subprocess-local/tests/linux-scope.spec.ts index ab69f4483b..8233e60a2f 100644 --- a/packages/subprocess/subprocess-local/tests/linux-scope.spec.ts +++ b/packages/subprocess/subprocess-local/tests/linux-scope.spec.ts @@ -84,6 +84,20 @@ function unitState(loadState: string, activeState: string) { return { status: 0, stdout: `LoadState=${loadState}\nActiveState=${activeState}\n`, stderr: '' } } +function activeUnitWithTasks(tasks: string) { + return { status: 0, stdout: `LoadState=loaded\nActiveState=active\nTasksCurrent=${tasks}\n`, stderr: '' } +} + +/** Deny every real process-group signal so a fake child never reaches a live host group. */ +function denyProcessGroups(): void { + vi.spyOn(process, 'kill').mockImplementation(() => { throw new Error('missing process group') }) +} + +/** Record the systemctl invocations a scope owner makes, succeeding unless a case overrides it. */ +function recordingSystemctl() { + return vi.fn((_command: string, _args: readonly string[]) => ({ status: 0, stdout: '', stderr: '' })) +} + function spec() { return { argv: ['tool', 'literal arg'], @@ -446,6 +460,7 @@ describe('Linux scope establishment and quiescence', () => { ['LoadState=loaded\nLoadState=loaded\nActiveState=active\n', 'duplicate LoadState'], ['LoadState=loaded\n', 'incomplete state'], ['LoadState=loaded\nActiveState=inactive\nOther=value\n', 'incomplete state'], + ['LoadState=loaded\nActiveState=active\nTasksCurrent=0\nOther=value\n', 'incomplete state'], ] as const) { const launched = launch(async () => ({ status: 0, stdout, stderr: '' })) await expect(launched.result.owner.waitForExit()).rejects.toThrow(message) @@ -453,6 +468,68 @@ describe('Linux scope establishment and quiescence', () => { } }) + it('rejects a manager process count that is neither numeric nor the unset sentinel', async () => { + const launched = launch(async () => activeUnitWithTasks('many')) + await expect(launched.result.owner.waitForExit()).rejects.toThrow('non-numeric TasksCurrent') + launched.result.owner.cleanup?.() + }) + + it('releases an active scope left with no processes once its client has gone', async () => { + // Regression: the manager's empty cgroup never ends this unit on its own. + denyProcessGroups() + const spawnSync = recordingSystemctl() + const launched = launch(async () => activeUnitWithTasks('0'), { spawnSync: spawnSync as never }) + launched.result.owner.signal('SIGKILL') + launched.child.exit(null, 'SIGKILL') + await expect(launched.result.owner.waitForExit()).resolves.toBeUndefined() + expect(spawnSync.mock.calls.map(call => call[1])).toEqual([ + ['--user', 'kill', '--kill-whom=all', '--signal=SIGKILL', expect.stringMatching(/\.scope$/u)], + ['--user', 'stop', expect.stringMatching(/\.scope$/u)], + ]) + launched.result.owner.cleanup?.() + }) + + it('concludes the empty range even when releasing the leftover scope fails', async () => { + denyProcessGroups() + const spawnSync = recordingSystemctl() + .mockImplementationOnce(() => ({ status: 0, stdout: '', stderr: '' })) + .mockImplementationOnce(() => { throw new Error('systemctl is gone') }) + const launched = launch(async () => activeUnitWithTasks('0'), { spawnSync: spawnSync as never }) + launched.result.owner.signal('SIGKILL') + launched.child.exit(null, 'SIGKILL') + await expect(launched.result.owner.waitForExit()).resolves.toBeUndefined() + launched.result.owner.cleanup?.() + }) + + it('keeps waiting while the client still owns an active scope with no processes', async () => { + denyProcessGroups() + const spawnSync = recordingSystemctl() + const states = [activeUnitWithTasks('0'), unloadedUnit()] + const launched = launch(async () => states.shift() ?? unloadedUnit(), { + spawnSync: spawnSync as never, + }) + launched.result.owner.signal('SIGTERM') + await expect(launched.result.owner.waitForExit()).resolves.toBeUndefined() + expect(spawnSync.mock.calls.map(call => call[1]?.[1])).toEqual(['kill']) + launched.result.owner.cleanup?.() + }) + + it('keeps waiting for an active empty scope no termination has requested', async () => { + const states = [activeUnitWithTasks('0'), unloadedUnit()] + const launched = launch(async () => states.shift() ?? unloadedUnit()) + await expect(launched.result.owner.waitForExit()).resolves.toBeUndefined() + expect(launched.spawnSync).not.toHaveBeenCalled() + launched.result.owner.cleanup?.() + }) + + it('treats an unset process count as unknown and keeps waiting', async () => { + const states = [activeUnitWithTasks('[not set]'), unloadedUnit()] + const launched = launch(async () => states.shift() ?? unloadedUnit()) + await expect(launched.result.owner.waitForExit()).resolves.toBeUndefined() + expect(launched.spawnSync).not.toHaveBeenCalled() + launched.result.owner.cleanup?.() + }) + it('keeps signal failures scoped to final kill proof and stays idempotent after stop', async () => { const spawnSync = vi.fn() .mockReturnValueOnce({ status: 1, stdout: '', stderr: '' }) From ac5f851095819b90247c25ee7b0209c1245116f5 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:13 +0800 Subject: [PATCH 13/20] test(directory-picker): probe the folder dialog class instead of the platform A Windows Server image can answer CoCreateInstance(CLSID_FileOpenDialog) with CLASS_E_CLASSNOTAVAILABLE, so the win32 COM smoke has no dialog to open and the POSIX rejection expectation is the only proof that image can produce. Gate the pair on a PowerShell activation probe taken outside the code under test, so a GUID or vtable regression in src still fails the smoke instead of skipping it. --- .../tests/win32-dialog.spec.ts | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/packages/host/directory-picker-native/tests/win32-dialog.spec.ts b/packages/host/directory-picker-native/tests/win32-dialog.spec.ts index 8e7d6951b8..bab9f4c222 100644 --- a/packages/host/directory-picker-native/tests/win32-dialog.spec.ts +++ b/packages/host/directory-picker-native/tests/win32-dialog.spec.ts @@ -1,11 +1,12 @@ /** * Driver tests: the child-process message protocol mapped onto the promise, * the WM_CLOSE abort service (including the show-race retry and the kill - * last resort) against fakes, plus the real spawn plumbing — POSIX hosts - * prove the default path rejects cleanly (koffi cannot load ole32 there), - * and win32 hosts briefly open and auto-abort a real dialog. + * last resort) against fakes, plus the real spawn plumbing: hosts that cannot + * open the folder dialog prove the default path rejects cleanly and hosts + * that can briefly open and auto-abort a real one. */ +import { spawnSync } from 'node:child_process' import { EventEmitter } from 'node:events' import { describe, expect, it, vi } from 'vitest' import { pickWin32Directory, type Win32DialogInternals, type Win32DialogWorkerLike } from '../src/win32-dialog.ts' @@ -41,6 +42,28 @@ function harness(overrides: Partial = {}): Harness { const live = (): AbortSignal => new AbortController().signal +/** CLSID_FileOpenDialog, spelled out here so the probe below owns its own copy. */ +const CLSID_FILE_OPEN_DIALOG = 'dc1c5a9c-e88a-4dde-a5a1-60f82a20aef7' + +/** + * Whether this host can open the shell's folder dialog. Some Windows Server + * images answer `CoCreateInstance(CLSID_FileOpenDialog)` with + * CLASS_E_CLASSNOTAVAILABLE (0x80040111) instead of an object, so no dialog + * can ever appear there. PowerShell activates the class outside the code + * under test, which keeps a GUID or vtable regression in `src` failing the + * smoke below instead of silently skipping it. Every exception from that + * activation reads as refusal, so a host that fails the probe for another + * reason only loses the real-dialog case; a probe that cannot run at all — + * no `powershell.exe` — keeps the win32 assumption. + */ +const opensRealDialog = process.platform === 'win32' && spawnSync('powershell.exe', [ + '-NoLogo', + '-NoProfile', + '-NonInteractive', + '-Command', + `try { [void][Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]'${CLSID_FILE_OPEN_DIALOG}')) } catch { exit 1 }`, +], { encoding: 'utf8' }).status !== 1 + describe('pickWin32Directory', () => { it('resolves the selected path and the cancellation null', async () => { const first = harness() @@ -145,15 +168,18 @@ describe('pickWin32Directory', () => { expect(close.mock.calls.length).toBeGreaterThan(10) }) - // POSIX hosts exercise the REAL default plumbing end to end: the tsx-bootstrapped - // worker spawns, loads koffi, fails to load ole32.dll, and reports the error. - it.skipIf(process.platform === 'win32')('rejects through the real worker where the Win32 surface is unavailable', async () => { + // Hosts without the folder dialog exercise the REAL default plumbing end to + // end: the tsx-bootstrapped worker spawns, loads koffi, and reports the + // refusal — ole32.dll fails to load on POSIX, and a Windows Server image + // that refuses the dialog class reports the same rejection. + it.skipIf(opensRealDialog)('rejects through the real worker where the Win32 surface is unavailable', async () => { await expect(pickWin32Directory(live())).rejects.toThrow('win32 folder dialog failed') }, 30_000) - // win32 hosts run the true COM smoke instead: a real dialog opens briefly - // and the abort service closes it (the same lever a disconnecting client pulls). - it.skipIf(process.platform !== 'win32')('opens and abort-closes a real dialog', async () => { + // Hosts with the dialog run the true COM smoke instead: a real dialog opens + // briefly and the abort service closes it (the same lever a disconnecting + // client pulls). + it.skipIf(!opensRealDialog)('opens and abort-closes a real dialog', async () => { const controller = new AbortController() setTimeout(() => { controller.abort() From df709f442c4ec05640f928ccd81ded6aaa28fc59 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:14 +0800 Subject: [PATCH 14/20] test(subprocess): align the teardown race with the settlement contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `disposal contains a spawn-failure rejection that races teardown` asserted one winner of the race. A bootstrap that publishes its pre-exec failure rejects with that failure, and a teardown that stops the bootstrap first settles as the requested termination — the recorded failure only outranks the stop when it was published before the stop landed. Assert the contract instead: a rejection carries the failure, a stop carries SIGTERM, which only the Linux scope records because the win32 job owner and the fallback launcher both reject the start. The two terminal cases that drive a mocked PTY exit state why they pin `internals.platform`: under the host's native scope the mocked exit races the scope bootstrap and fails as `terminal scope exited before its bootstrap consumed the launch request`. --- .../subprocess-local/tests/local.spec.ts | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/subprocess/subprocess-local/tests/local.spec.ts b/packages/subprocess/subprocess-local/tests/local.spec.ts index 135638f54a..a13f589de1 100644 --- a/packages/subprocess/subprocess-local/tests/local.spec.ts +++ b/packages/subprocess/subprocess-local/tests/local.spec.ts @@ -393,6 +393,8 @@ describe('LocalSubprocessRuntime', () => { const ctx = new Context() const fiber = await ctx.plugin(IsolatedLocalSubprocessRuntime) const service = ctx.subprocess as InstanceType + // Pins the containment choice: with the host's native scope a mocked PTY + // exit races the scope bootstrap. service.internals = { platform: 'darwin' } const handle = await ctx.subprocess.spawnTerminal({ argv: ['shell'], cwd: process.cwd(), rows: 24, cols: 80, graceMs: 1, @@ -600,6 +602,8 @@ describe('LocalSubprocessRuntime', () => { ctx.logger.error = ((error: unknown) => { disposalErrors.push(error) }) as typeof ctx.logger.error const fiber = await ctx.plugin(IsolatedLocalSubprocessRuntime) const alive = new Set([124]) + // Pins the containment choice: with the host's native scope a mocked PTY + // exit races the scope bootstrap. ;(ctx.subprocess as InstanceType).internals = { platform: 'darwin' } ;(ctx.subprocess as InstanceType).terminalInspector = { foregroundPgid: () => 123, @@ -878,10 +882,26 @@ describe('LocalSubprocessRuntime', () => { const ctx = new Context() const fiber = await ctx.plugin(LocalSubprocessRuntime) // Dispose before the rejection continuation removes the handle from the - // live set, so teardown itself must swallow the rejected done. + // live set, so teardown itself must swallow the rejected done. Two + // settlements are valid and the winner is a race: a bootstrap that + // publishes its pre-exec failure rejects with that failure, and a teardown + // that stops the bootstrap first settles as the requested termination — + // the recorded failure only outranks the stop when it was published before + // the stop landed. const handle = ctx.subprocess.spawn(spec('true', { cwd: '/nonexistent-dir-dsh-subprocess-test' })) await fiber.dispose() - await expect(handle.done).rejects.toThrow() + const settlement = await handle.done.then( + outcome => ({ kind: 'stopped' as const, outcome }), + (error: unknown) => ({ kind: 'failed' as const, error }), + ) + if (settlement.kind === 'failed') { + expect(settlement.error).toBeInstanceOf(Error) + } else { + // Only the Linux scope records a stop this way: the win32 job owner + // rejects a cancelled start and the fallback launcher rejects the ENOENT, + // so neither can produce the stopped branch. + expect(settlement.outcome.signal).toBe('SIGTERM') + } }) it('loading a second implementation throws (one processes service per context — cordis standard)', async () => { From f45ca2d9acdbb295e13b6e7eac7daa717ef50470 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:15 +0800 Subject: [PATCH 15/20] test: drop two host-paced assumptions the hosted image exposes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACP plugin-config case drove a real dispose with 150ms graces. On the hosted image the SIGKILL escalation landed while the managed scope could not take the signal — systemctl failed the kill ("Failed to send signal SIGKILL to auxiliary processes: Invalid argument") — so the run reported a teardown failure the configuration never asked for. The case asserts that config graces reach the real run, so the values only need to clear the host's scope handling. Its mock refuses stdin EOF and SIGTERM by design, so the case waits out both graces and carries its own budget for that fixed cost. Both illegal-UTF-8 residual cases relied on a plain sched_yield leaving each write as its own read chunk. A starved reader coalesces hundreds of bytes into one chunk, which inflates the measured peak for correct accounting too: the hosted image measured 2563 against the 2048 bound. Pace with a real sleep so each byte is its own chunk everywhere, and keep each payload above the bound (3200 bytes; 1100 CESU-8 sequences) so a raw-byte undercount still flushes past it instead of only at EOF. --- .../code-runtime-python/tests/runtime.spec.ts | 29 ++++++++++++++----- .../subagent-acp/tests/subagent-acp.spec.ts | 20 +++++++++---- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/packages/experimental/code-runtime-python/tests/runtime.spec.ts b/packages/experimental/code-runtime-python/tests/runtime.spec.ts index 4fa4f225d4..b89ec692b0 100644 --- a/packages/experimental/code-runtime-python/tests/runtime.spec.ts +++ b/packages/experimental/code-runtime-python/tests/runtime.spec.ts @@ -1517,10 +1517,14 @@ describe('PythonCodeRuntime — programs and bindings', () => { const { runtime } = await setup({ maxLogBytes: 3072, maxWallMs: 30_000 }) result = await runtime.run({ program: [ - 'import os', - 'for _ in range(6000):', + 'import os, time', + // One byte per chunk on every host: a plain yield lets a loaded + // reader coalesce, and the coalesced chunk is what the bound below + // measures. The payload stays above the 2048 discriminator, so a + // raw-byte undercount still flushes the whole residual at EOF. + 'for _ in range(3200):', ' os.write(1, b"\\xff")', - ' os.sched_yield()', + ' time.sleep(0.001)', 'return None', ].join('\n'), bindings: [], @@ -1534,7 +1538,10 @@ describe('PythonCodeRuntime — programs and bindings', () => { // merged buffer stays well under 2048. A raw-byte undercount would let it // reach ~3072 before flushing, so 2048 discriminates. expect(maxConcat).toBeLessThan(2048) - }) + // The paced payload costs ~3.2s deterministically, which is above the + // 5000ms default the local unit entry grants, so the case carries its own + // bound instead of relying on the lane to widen it. + }, 20_000) it('charges a structurally-valid but illegal UTF-8 sequence its U+FFFD-decoded cost', async () => { // A CESU-8 lone surrogate `ED A0 80` is structurally well-formed (a 3-byte @@ -1559,12 +1566,15 @@ describe('PythonCodeRuntime — programs and bindings', () => { const { runtime } = await setup({ maxLogBytes: 3072, maxWallMs: 30_000 }) result = await runtime.run({ program: [ - 'import os', + 'import os, time', 'seq = (0xed, 0xa0, 0x80)', - 'for _ in range(2000):', + // 1100 sequences are 3300 raw bytes, past the 3072-byte budget a + // raw-byte undercount reaches, so the undercount flushes above the + // 2048 discriminator instead of only at EOF. + 'for _ in range(1100):', ' for b in seq:', ' os.write(1, bytes((b,)))', - ' os.sched_yield()', + ' time.sleep(0.001)', 'return None', ].join('\n'), bindings: [], @@ -1579,7 +1589,10 @@ describe('PythonCodeRuntime — programs and bindings', () => { // largest merged buffer stays well under 2048. Charging the structural width // 3 would need ~1024 raw bytes, tripling the peak past 2048. expect(maxConcat).toBeLessThan(2048) - }) + // The paced payload costs ~3.3s deterministically, which is above the + // 5000ms default the local unit entry grants, so the case carries its own + // bound instead of relying on the lane to widen it. + }, 20_000) it('charges a lone surrogate its full six escaped bytes, not three', async () => { // A forged `log` frame carrying `\ud800` escapes materializes lone diff --git a/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts b/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts index 1de31c00ae..cea3e4536f 100644 --- a/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts +++ b/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts @@ -1412,17 +1412,27 @@ describe('dsh-subagent-acp', () => { args: [mockServer], permission: 'reject', env: { MOCK_TRAP_SIGTERM: '1', MOCK_TEXT: 'x', MOCK_READY_FILE: ready }, - disposeEofGraceMs: 150, - disposeGraceMs: 150, + // The graces are wall-clock budgets for the managed scope's teardown on a + // shared host. At 150ms the hosted image escalated while the scope could + // not take the signal and systemctl failed the kill ("Failed to send + // signal SIGKILL to auxiliary processes: Invalid argument"), surfacing as + // a teardown failure the configuration never asked for. The case asserts + // that config graces reach the real run, so the value only has to clear + // the host's scope handling. + disposeEofGraceMs: 5_000, + disposeGraceMs: 5_000, }) const run = await ctx.subagents.start('acp', request()) expect(start).toHaveBeenCalledExactlyOnceWith(expect.anything(), expect.objectContaining({ - disposeEofGraceMs: 150, - disposeGraceMs: 150, + disposeEofGraceMs: 5_000, + disposeGraceMs: 5_000, })) await waitForFile(ready, task.timeout) await expect(run.dispose()).resolves.toBeUndefined() - }) + // The trapped child refuses stdin EOF, so the ladder waits out the EOF + // grace and then the SIGTERM grace (10s) before the SIGKILL settles it. + // That fixed cost is above the 5000ms default the local unit entry grants. + }, 30_000) it('rejects a dispose grace outside the Node timer range at load', async () => { for (const bad of [ From 45c10ed2ce022d32ee52f057a3d9afe80051b57f Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:16 +0800 Subject: [PATCH 16/20] ci: give the Linux coverage lane the hosted image's teardown budget The managed-scope disposal cases in subprocess-local and bash-sandbox run past the 5000ms default when this lane's four partitions, six workers per gate, and sibling gates share one host. The same commit stays inside the budget on the in-house pool: the hosted image exceeded it (run 34449848541) and the lane is green with 90000 (run 34457655892). The Windows coverage lane already grants the same budget for the same class of teardown. --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c06a52d15..57ed55c36d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,6 +121,14 @@ jobs: DSH_COVERAGE_MAX_WORKERS: '6' DSH_COVERAGE_PARTITIONS: '4' DSH_GATE_CONCURRENCY: '3' + # Managed-scope teardown cases run past the default per-test budget when + # this lane's partitions, workers, and sibling gates share one host: the + # disposal cases in subprocess-local and bash-sandbox exceeded 5000ms on + # the hosted image (run 34449848541) while the same commit stayed inside + # the budget on the in-house pool; the lane is green with this value + # (run 34457655892). The Windows coverage lane grants the same budget for + # the same reason. + DSH_COVERAGE_TEST_TIMEOUT_MS: '90000' # A gate failure aborts the sibling gate instead of waiting out its # multi-minute instrumented run. DSH_GATE_FAIL_FAST: '1' From 87f32dc8ca8e0e7e297a6bba517612f9a8c0b16a Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:16 +0800 Subject: [PATCH 17/20] docs(notes): record the hosted-image assumptions the coverage suite names The failover leg runs this suite on pools this repository does not own, and the coverage lanes failed there on host properties their cases never named: the containment a mocked PTY exit raced, the grace a managed scope needs before it can take a SIGKILL, reader coalescing in the illegal-UTF-8 residual cases, and a Windows image that refuses the folder dialog class. Record what each case pins now, what was rejected, and the runs that measured it, so the next pool change starts from those host dependencies instead of re-diagnosing them. --- ...10-hosted-image-test-assumptions.i18n.yaml | 6 +++ ...026-09-10-hosted-image-test-assumptions.md | 39 +++++++++++++++++++ ...-09-10-hosted-image-test-assumptions.zh.md | 39 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 .agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.i18n.yaml create mode 100644 .agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.md create mode 100644 .agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.zh.md diff --git a/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.i18n.yaml b/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.i18n.yaml new file mode 100644 index 0000000000..52a4c5dd7f --- /dev/null +++ b/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# 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/testing/2026-09-10-hosted-image-test-assumptions.md +2026-09-10-hosted-image-test-assumptions.md: e6cd1ef37dce318920d75545f816006a98989076 +2026-09-10-hosted-image-test-assumptions.zh.md: e60910aebdc2952a2bfb6ab25963a973ea41ac87 diff --git a/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.md b/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.md new file mode 100644 index 0000000000..e6cd1ef37d --- /dev/null +++ b/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.md @@ -0,0 +1,39 @@ +# Agent Note: Hosted-image assumptions in the coverage suite + +Status: implemented + +English | [中文](2026-09-10-hosted-image-test-assumptions.zh.md) + +## Problem + +The [failover leg](../process/2026-09-09-blacksmith-failover-leg.md) runs this suite on pools this repository does not own — Blacksmith's ephemeral images, and the in-house `vm-backup` and `dsh-win-ci` standbys. On the hosted image the coverage lanes failed on host properties their cases never named: whether the host offered a usable user-systemd scope decided which containment a mocked PTY exit raced; the wall-clock grace a managed scope needed before it could take a `SIGKILL` was below what a loaded image provides; a starved reader coalesced writes the illegal-UTF-8 residual cases assumed arrived as separate chunks; and a Windows Server image refuses `CoCreateInstance(CLSID_FileOpenDialog)` outright. + +## Decision + +Every case names the host property it depends on, so the same revision reports the same verdict on the in-house pool and on a hosted image. + +Terminal cases that drive a mocked PTY exit pin the containment they need (`internals = { platform: 'darwin' }` in `packages/subprocess/subprocess-local/tests/local.spec.ts`); under the host's native scope the mocked exit races the scope bootstrap and fails as `terminal scope exited before its bootstrap consumed the launch request`. Mocking the `linux-scope.ts` probes to reach the same path was removed: the platform pin skips both probes, so the mock could not change the selected path. + +`disposal contains a spawn-failure rejection that races teardown` asserts the settlement contract instead of one winner of the race: a bootstrap that published its pre-exec failure rejects with that failure, and a teardown that stopped the bootstrap first settles as the requested `SIGTERM`. Only the Linux scope records the stopped arm, because the win32 job owner turns a cancelled start into a rejection and the fallback launcher rejects the missing directory. + +`plugin-config dispose graces reach the real ACP run` configures 5000ms dispose graces. At 150ms the hosted image escalated while the scope could not take the signal — `systemctl` failed the kill (`Failed to send signal SIGKILL to auxiliary processes: Invalid argument`) and the teardown reported a failure the configuration never asked for. Its mock refuses stdin EOF and `SIGTERM` by design, so the case waits out both graces (~10s) and carries a 30s case budget, above the 5000ms default the local unit entry grants. + +Both illegal-UTF-8 residual cases in `packages/experimental/code-runtime-python/tests/runtime.spec.ts` pace their writes with `time.sleep(0.001)`: `os.sched_yield()` lets a loaded reader coalesce the writes into one chunk, and the coalesced chunk is what the wrapped `Buffer.concat` measures (the hosted image measured 2563 against the 2048 bound with a correct implementation). Their payloads stay above that bound — 3200 bytes for the `0xFF` case and 1100 `ED A0 80` sequences, 3300 raw bytes, for the CESU-8 case, past the 3072-byte budget a raw-byte undercount reaches — so the undercount still flushes above 2048. Each carries a 20s case budget for the paced writes plus the interpreter start. + +The Linux coverage lane grants `DSH_COVERAGE_TEST_TIMEOUT_MS: '90000'`, matching the Windows coverage lane, because the disposal cases in `subprocess-local` and `bash-sandbox` exceed the 5000ms default when the lane's partitions, workers, and sibling gates share one host. + +The Windows folder-dialog smoke probes `CoCreateInstance(CLSID_FileOpenDialog)` through PowerShell instead of gating on `process.platform`. An image that answers `CLASS_E_CLASSNOTAVAILABLE` (0x80040111) runs the clean-rejection case and skips the real-dialog case, so `win32-dialog.ts` keeps its file coverage without a host that can open a dialog. Every exception from that activation reads as refusal, so a host failing the probe for another reason only loses the real-dialog case; a probe that cannot run at all keeps the win32 assumption. + +## Alternatives considered + +**Excluding the coverage lanes from the hosted leg.** Rejected: the leg exists to run the same suite on another pool, and the failures named real host dependencies rather than a suite the pool cannot support. + +**Raising only the lane's per-test budget.** Rejected: a wider budget does not change the cases whose cost or behaviour is deterministic — a trapped ACP child still waits out both graces, and a coalesced reader still inflates the measured peak. + +**Keeping the `linux-scope.ts` probe mocks.** Rejected as inert: the platform pin selects the fallback path before either probe is called, so the mock changed no execution path. + +**Cutting the illegal-UTF-8 payloads to keep the cases fast.** Rejected: below the 2048 bound the assertion can no longer fail for the undercount it names, which leaves the regression unguarded. + +## Consequences + +The suite's verdict no longer depends on which pool served the lane, at the cost of fixtures pinned to one containment choice: the `linux-scope` and win32-job paths keep their own dedicated cases instead of being reached through these ones. The ACP dispose case costs about 10s of wall clock per run and each residual case about 3.5s, all deterministic rather than host-paced. Hosted-image evidence: [run 34449848541](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34449848541) failed on these cases, [run 34457655892](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34457655892) is green with this diff plus the 90000ms lane budget, and `windows node 24 / coverage` is green on five consecutive hosted runs, where the probe reports the refusal (`clsid-probe=refused`). diff --git a/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.zh.md b/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.zh.md new file mode 100644 index 0000000000..e60910aebd --- /dev/null +++ b/.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.zh.md @@ -0,0 +1,39 @@ +# Agent Note: coverage 套件里的托管镜像假设 + +Status: implemented + +[English](2026-09-10-hosted-image-test-assumptions.md) | 中文 + +## 问题 + +[故障切换支路](../process/2026-09-09-blacksmith-failover-leg.zh.md)会把这套测试跑在本仓库不拥有的池上——Blacksmith 的临时镜像,以及自有的 `vm-backup` 与 `dsh-win-ci` 备用池。在托管镜像上,coverage 各通道的失败来自用例从未点明的宿主属性:宿主是否提供可用的用户级 systemd scope,决定了被 mock 的 PTY 退出会与哪种 containment 竞争;托管 scope 接受 `SIGKILL` 之前所需的墙钟宽限,低于负载镜像实际提供的量;读端被抢占时会把非法 UTF-8 残余用例假定为独立分块的写入合并成一个分块;以及 Windows Server 镜像直接拒绝 `CoCreateInstance(CLSID_FileOpenDialog)`。 + +## 决策 + +每个用例都点明它依赖的宿主属性,因此同一份修订在自有池与托管镜像上给出同样的结论。 + +驱动被 mock 的 PTY 退出的终端用例钉死自己需要的 containment(`packages/subprocess/subprocess-local/tests/local.spec.ts` 中的 `internals = { platform: 'darwin' }`);在宿主的原生 scope 下,被 mock 的退出会与 scope 的 bootstrap 竞争,并以 `terminal scope exited before its bootstrap consumed the launch request` 失败。为走到同一路径而 mock `linux-scope.ts` 的探针已被删除:平台钉死会让两个探针都不被调用,因此该 mock 无法改变选中的路径。 + +`disposal contains a spawn-failure rejection that races teardown` 断言结算契约,而不是这场竞争的某一方获胜:已经发布其 pre-exec 失败的 bootstrap 以该失败 reject,先停住 bootstrap 的 teardown 则以被请求的 `SIGTERM` 结算。只有 Linux scope 会记录停止这一支,因为 win32 job owner 会把被取消的启动转成 rejection,fallback 启动器则因目录缺失而 reject。 + +`plugin-config dispose graces reach the real ACP run` 配置 5000ms 的 dispose 宽限。在 150ms 时,托管镜像在 scope 还无法接受信号时就升级了信号——`systemctl` 的 kill 失败(`Failed to send signal SIGKILL to auxiliary processes: Invalid argument`),teardown 上报了一个配置从未要求的失败。该用例的 mock 按设计既拒绝 stdin EOF 也拒绝 `SIGTERM`,因此用例会等满两个宽限(约 10s),并自带 30s 的用例预算,高于本地单测入口授予的 5000ms 默认值。 + +`packages/experimental/code-runtime-python/tests/runtime.spec.ts` 的两个非法 UTF-8 残余用例都用 `time.sleep(0.001)` 控制写入节奏:`os.sched_yield()` 会让被抢占的读端把多次写入合并成一个分块,而被包裹的 `Buffer.concat` 测量的正是该分块(在正确实现下,托管镜像测得 2563,超过了 2048 的界)。两个用例的载荷都保持在该界之上——`0xFF` 用例 3200 字节,CESU-8 用例 1100 个 `ED A0 80` 序列(3300 原始字节,超过按原始字节计费会触及的 3072 字节预算)——因此少计仍然会在 2048 之上触发 flush。两者各自带有 20s 的用例预算,容纳带节奏的写入与解释器启动。 + +Linux coverage 通道授予 `DSH_COVERAGE_TEST_TIMEOUT_MS: '90000'`,与 Windows coverage 通道一致,因为当该通道的分区、worker 与同级门禁共用一个宿主时,`subprocess-local` 与 `bash-sandbox` 的处置用例会超过 5000ms 默认值。 + +Windows 文件夹对话框冒烟测试改为通过 PowerShell 探测 `CoCreateInstance(CLSID_FileOpenDialog)`,而不再按 `process.platform` 分流。回答 `CLASS_E_CLASSNOTAVAILABLE`(0x80040111)的镜像会跑干净的拒绝用例并跳过真实对话框用例,因此 `win32-dialog.ts` 在没有可开对话框的宿主上仍保有文件覆盖率。该激活过程抛出的任何异常都按拒绝解读,因此因其它原因探测失败的宿主只会失去真实对话框用例;完全无法运行的探测则保留 win32 假设。 + +## 备选方案 + +**把 coverage 通道排除出托管支路。** 否决:该支路的意义就是把同一套测试跑在另一个池上,而这些失败点出的是真实的宿主依赖,不是一个该池无法支撑的套件。 + +**只抬高通道的每用例预算。** 否决:更宽的预算改变不了那些成本或行为确定的用例——被 trap 的 ACP 子进程仍然会等满两个宽限,被合并的读端仍然会抬高测得的峰值。 + +**保留 `linux-scope.ts` 的探针 mock。** 因无效而否决:平台钉死会在任一探针被调用前就选中 fallback 路径,因此该 mock 没有改变任何执行路径。 + +**削减非法 UTF-8 的载荷以让用例更快。** 否决:低于 2048 的界之后,断言再也无法为它所点名的少计而失败,等于让该回归失去守护。 + +## 后果 + +套件的结论不再取决于哪个池服务了这条通道,代价是被钉在某一 containment 选择上的 fixture:`linux-scope` 与 win32-job 两条路径仍由各自的专用用例覆盖,而不是经由这些用例抵达。ACP 处置用例每次运行约 10s 墙钟,每个残余用例约 3.5s,且都是确定成本而非随宿主浮动。托管镜像证据:[run 34449848541](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34449848541) 在这些用例上失败,[run 34457655892](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34457655892) 在本改动加 90000ms 通道预算下转绿,`windows node 24 / coverage` 在连续五次托管运行中为绿,其中探针报告拒绝(`clsid-probe=refused`)。 From c4c2d163083c701e8584b4f0cb1d955b68ab2bef Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:14 +0800 Subject: [PATCH 18/20] test(subprocess): align the teardown race with the settlement contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `disposal contains a spawn-failure rejection that races teardown` asserted one winner of the race. A bootstrap that publishes its pre-exec failure rejects with that failure, and a teardown that stops the bootstrap first settles as the requested termination — the recorded failure only outranks the stop when it was published before the stop landed. Assert the contract instead: a rejection carries the failure, a stop carries SIGTERM, which only the Linux scope records because the win32 job owner and the fallback launcher both reject the start. The two terminal cases that drive a mocked PTY exit state why they pin `internals.platform`: under the host's native scope the mocked exit races the scope bootstrap and fails as `terminal scope exited before its bootstrap consumed the launch request`. (cherry picked from commit fee113eb9a5632555304858d2bf6355588a3aaf0) --- .../subprocess-local/tests/local.spec.ts | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/subprocess/subprocess-local/tests/local.spec.ts b/packages/subprocess/subprocess-local/tests/local.spec.ts index 135638f54a..a13f589de1 100644 --- a/packages/subprocess/subprocess-local/tests/local.spec.ts +++ b/packages/subprocess/subprocess-local/tests/local.spec.ts @@ -393,6 +393,8 @@ describe('LocalSubprocessRuntime', () => { const ctx = new Context() const fiber = await ctx.plugin(IsolatedLocalSubprocessRuntime) const service = ctx.subprocess as InstanceType + // Pins the containment choice: with the host's native scope a mocked PTY + // exit races the scope bootstrap. service.internals = { platform: 'darwin' } const handle = await ctx.subprocess.spawnTerminal({ argv: ['shell'], cwd: process.cwd(), rows: 24, cols: 80, graceMs: 1, @@ -600,6 +602,8 @@ describe('LocalSubprocessRuntime', () => { ctx.logger.error = ((error: unknown) => { disposalErrors.push(error) }) as typeof ctx.logger.error const fiber = await ctx.plugin(IsolatedLocalSubprocessRuntime) const alive = new Set([124]) + // Pins the containment choice: with the host's native scope a mocked PTY + // exit races the scope bootstrap. ;(ctx.subprocess as InstanceType).internals = { platform: 'darwin' } ;(ctx.subprocess as InstanceType).terminalInspector = { foregroundPgid: () => 123, @@ -878,10 +882,26 @@ describe('LocalSubprocessRuntime', () => { const ctx = new Context() const fiber = await ctx.plugin(LocalSubprocessRuntime) // Dispose before the rejection continuation removes the handle from the - // live set, so teardown itself must swallow the rejected done. + // live set, so teardown itself must swallow the rejected done. Two + // settlements are valid and the winner is a race: a bootstrap that + // publishes its pre-exec failure rejects with that failure, and a teardown + // that stops the bootstrap first settles as the requested termination — + // the recorded failure only outranks the stop when it was published before + // the stop landed. const handle = ctx.subprocess.spawn(spec('true', { cwd: '/nonexistent-dir-dsh-subprocess-test' })) await fiber.dispose() - await expect(handle.done).rejects.toThrow() + const settlement = await handle.done.then( + outcome => ({ kind: 'stopped' as const, outcome }), + (error: unknown) => ({ kind: 'failed' as const, error }), + ) + if (settlement.kind === 'failed') { + expect(settlement.error).toBeInstanceOf(Error) + } else { + // Only the Linux scope records a stop this way: the win32 job owner + // rejects a cancelled start and the fallback launcher rejects the ENOENT, + // so neither can produce the stopped branch. + expect(settlement.outcome.signal).toBe('SIGTERM') + } }) it('loading a second implementation throws (one processes service per context — cordis standard)', async () => { From 114e8c9b0950ba545915ea9d2e057521f3dfccc8 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:15 +0800 Subject: [PATCH 19/20] test: drop two host-paced assumptions the hosted image exposes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACP plugin-config case drove a real dispose with 150ms graces. On the hosted image the SIGKILL escalation landed while the managed scope could not take the signal — systemctl failed the kill ("Failed to send signal SIGKILL to auxiliary processes: Invalid argument") — so the run reported a teardown failure the configuration never asked for. The case asserts that config graces reach the real run, so the values only need to clear the host's scope handling. Its mock refuses stdin EOF and SIGTERM by design, so the case waits out both graces and carries its own budget for that fixed cost. Both illegal-UTF-8 residual cases relied on a plain sched_yield leaving each write as its own read chunk. A starved reader coalesces hundreds of bytes into one chunk, which inflates the measured peak for correct accounting too: the hosted image measured 2563 against the 2048 bound. Pace with a real sleep so each byte is its own chunk everywhere, and keep each payload above the bound (3200 bytes; 1100 CESU-8 sequences) so a raw-byte undercount still flushes past it instead of only at EOF. (cherry picked from commit 30e0c3ff0415befe3652a55611df8a18d82c18bb) --- .../code-runtime-python/tests/runtime.spec.ts | 29 ++++++++++++++----- .../subagent-acp/tests/subagent-acp.spec.ts | 20 +++++++++---- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/packages/experimental/code-runtime-python/tests/runtime.spec.ts b/packages/experimental/code-runtime-python/tests/runtime.spec.ts index 4fa4f225d4..b89ec692b0 100644 --- a/packages/experimental/code-runtime-python/tests/runtime.spec.ts +++ b/packages/experimental/code-runtime-python/tests/runtime.spec.ts @@ -1517,10 +1517,14 @@ describe('PythonCodeRuntime — programs and bindings', () => { const { runtime } = await setup({ maxLogBytes: 3072, maxWallMs: 30_000 }) result = await runtime.run({ program: [ - 'import os', - 'for _ in range(6000):', + 'import os, time', + // One byte per chunk on every host: a plain yield lets a loaded + // reader coalesce, and the coalesced chunk is what the bound below + // measures. The payload stays above the 2048 discriminator, so a + // raw-byte undercount still flushes the whole residual at EOF. + 'for _ in range(3200):', ' os.write(1, b"\\xff")', - ' os.sched_yield()', + ' time.sleep(0.001)', 'return None', ].join('\n'), bindings: [], @@ -1534,7 +1538,10 @@ describe('PythonCodeRuntime — programs and bindings', () => { // merged buffer stays well under 2048. A raw-byte undercount would let it // reach ~3072 before flushing, so 2048 discriminates. expect(maxConcat).toBeLessThan(2048) - }) + // The paced payload costs ~3.2s deterministically, which is above the + // 5000ms default the local unit entry grants, so the case carries its own + // bound instead of relying on the lane to widen it. + }, 20_000) it('charges a structurally-valid but illegal UTF-8 sequence its U+FFFD-decoded cost', async () => { // A CESU-8 lone surrogate `ED A0 80` is structurally well-formed (a 3-byte @@ -1559,12 +1566,15 @@ describe('PythonCodeRuntime — programs and bindings', () => { const { runtime } = await setup({ maxLogBytes: 3072, maxWallMs: 30_000 }) result = await runtime.run({ program: [ - 'import os', + 'import os, time', 'seq = (0xed, 0xa0, 0x80)', - 'for _ in range(2000):', + // 1100 sequences are 3300 raw bytes, past the 3072-byte budget a + // raw-byte undercount reaches, so the undercount flushes above the + // 2048 discriminator instead of only at EOF. + 'for _ in range(1100):', ' for b in seq:', ' os.write(1, bytes((b,)))', - ' os.sched_yield()', + ' time.sleep(0.001)', 'return None', ].join('\n'), bindings: [], @@ -1579,7 +1589,10 @@ describe('PythonCodeRuntime — programs and bindings', () => { // largest merged buffer stays well under 2048. Charging the structural width // 3 would need ~1024 raw bytes, tripling the peak past 2048. expect(maxConcat).toBeLessThan(2048) - }) + // The paced payload costs ~3.3s deterministically, which is above the + // 5000ms default the local unit entry grants, so the case carries its own + // bound instead of relying on the lane to widen it. + }, 20_000) it('charges a lone surrogate its full six escaped bytes, not three', async () => { // A forged `log` frame carrying `\ud800` escapes materializes lone diff --git a/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts b/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts index 1de31c00ae..cea3e4536f 100644 --- a/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts +++ b/packages/subagent/subagent-acp/tests/subagent-acp.spec.ts @@ -1412,17 +1412,27 @@ describe('dsh-subagent-acp', () => { args: [mockServer], permission: 'reject', env: { MOCK_TRAP_SIGTERM: '1', MOCK_TEXT: 'x', MOCK_READY_FILE: ready }, - disposeEofGraceMs: 150, - disposeGraceMs: 150, + // The graces are wall-clock budgets for the managed scope's teardown on a + // shared host. At 150ms the hosted image escalated while the scope could + // not take the signal and systemctl failed the kill ("Failed to send + // signal SIGKILL to auxiliary processes: Invalid argument"), surfacing as + // a teardown failure the configuration never asked for. The case asserts + // that config graces reach the real run, so the value only has to clear + // the host's scope handling. + disposeEofGraceMs: 5_000, + disposeGraceMs: 5_000, }) const run = await ctx.subagents.start('acp', request()) expect(start).toHaveBeenCalledExactlyOnceWith(expect.anything(), expect.objectContaining({ - disposeEofGraceMs: 150, - disposeGraceMs: 150, + disposeEofGraceMs: 5_000, + disposeGraceMs: 5_000, })) await waitForFile(ready, task.timeout) await expect(run.dispose()).resolves.toBeUndefined() - }) + // The trapped child refuses stdin EOF, so the ladder waits out the EOF + // grace and then the SIGTERM grace (10s) before the SIGKILL settles it. + // That fixed cost is above the 5000ms default the local unit entry grants. + }, 30_000) it('rejects a dispose grace outside the Node timer range at load', async () => { for (const bad of [ From 03770494656e5162cea496826486f9ad23a4ac21 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 10 Sep 2026 17:36:16 +0800 Subject: [PATCH 20/20] ci: give the Linux coverage lane the hosted image's teardown budget The managed-scope disposal cases in subprocess-local and bash-sandbox run past the 5000ms default when this lane's four partitions, six workers per gate, and sibling gates share one host. The same commit stays inside the budget on the in-house pool: the hosted image exceeded it (run 34449848541) and the lane is green with 90000 (run 34457655892). The Windows coverage lane already grants the same budget for the same class of teardown. (cherry picked from commit bf3468adf0947a51f09d4a1aaae2865f3e50a2f2) --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c06a52d15..57ed55c36d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,6 +121,14 @@ jobs: DSH_COVERAGE_MAX_WORKERS: '6' DSH_COVERAGE_PARTITIONS: '4' DSH_GATE_CONCURRENCY: '3' + # Managed-scope teardown cases run past the default per-test budget when + # this lane's partitions, workers, and sibling gates share one host: the + # disposal cases in subprocess-local and bash-sandbox exceeded 5000ms on + # the hosted image (run 34449848541) while the same commit stayed inside + # the budget on the in-house pool; the lane is green with this value + # (run 34457655892). The Windows coverage lane grants the same budget for + # the same reason. + DSH_COVERAGE_TEST_TIMEOUT_MS: '90000' # A gate failure aborts the sibling gate instead of waiting out its # multi-minute instrumented run. DSH_GATE_FAIL_FAST: '1'