Skip to content

fix(cli): reject invalid session list cursors - #5709

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/session-list-cursor-integer
Jun 23, 2026
Merged

fix(cli): reject invalid session list cursors#5709
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/session-list-cursor-integer

Conversation

@tt-a1i

@tt-a1i tt-a1i commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Tightens session-list cursor validation before calling SessionService.listSessions. REST / ACP HTTP and in-process ACP now reject negative cursor values, whitespace-only cursor strings, and values above Number.MAX_SAFE_INTEGER while preserving existing first-page handling for omitted or empty cursors.

The change keeps non-negative fractional cursors valid because session pagination cursors come from filesystem mtimeMs, which can include fractional milliseconds. Tests now cover the invalid edges and assert that fractional cursor values are forwarded without truncation.

Why it's needed

Session-list cursors are generated by SessionService.listSessions() from file mtimeMs values. The current handlers already reject non-numeric and non-finite cursors, but still accept finite values like -1 or 9007199254740992. Those are not valid page cursors: negative cursors can produce empty persisted pages, and unsafe numeric values can lose precision before filtering.

Rejecting these values keeps the REST / ACP HTTP and in-process ACP paths aligned, while still accepting cursor tokens that can actually be returned by the service.

Reviewer Test Plan

How to verify

Run:

npm test --workspace=packages/cli -- serve/server.test.ts -t cursor --coverage.enabled=false
npm test --workspace=packages/cli -- acp-integration/acpAgent.test.ts -t unstable_listSessions --coverage.enabled=false
npx prettier --check packages/cli/src/serve/server.ts packages/cli/src/serve/server.test.ts packages/cli/src/acp-integration/acpAgent.ts packages/cli/src/acp-integration/acpAgent.test.ts
npx eslint packages/cli/src/serve/server.ts packages/cli/src/serve/server.test.ts packages/cli/src/acp-integration/acpAgent.ts packages/cli/src/acp-integration/acpAgent.test.ts
npm run lint --workspace=packages/cli --if-present
git diff --check

Expected behavior: REST session-list requests with invalid cursors such as abc, -1, Infinity, 9007199254740992, or whitespace-only strings return 400 invalid_cursor. In-process ACP rejects the same invalid cursor values before constructing SessionService. Non-negative fractional cursors such as 1000123.456 remain valid and are forwarded without truncation.

Known unrelated local check failure:

npm run typecheck --workspace=packages/cli --if-present

This still fails in src/ui/components/BaseTextInput.tsx because local TypeScript cannot resolve ink/dom and ink/components/CursorContext; those files are outside this PR.

Evidence (Before & After)

N/A — non-UI API validation change. Unit tests cover the before/after behavior: invalid negative, unsafe, non-finite, non-numeric, and whitespace-only cursors are rejected; omitted and empty cursors keep first-page behavior; valid fractional mtimeMs cursors are preserved.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Environment (optional)

Local Node/npm workspace. I ran focused Vitest, Prettier check, targeted ESLint, workspace lint, and git diff --check. CLI typecheck has the unrelated ink/dom resolution failure noted above.

Risk & Scope

  • Main risk or tradeoff: Clients that send malformed finite cursors now receive validation errors instead of empty or imprecise pages. Cursor values returned by the service remain accepted, including fractional mtimeMs values.
  • Not validated / out of scope: Changing the cursor token format or adding a shared exported parser across packages.
  • Breaking changes / migration notes: None expected for valid clients. Omitted and empty cursors continue to mean first page.

Linked Issues

Fixes #5708

AI Assistance Disclosure

I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.

中文说明

这个 PR 做了什么

在调用 SessionService.listSessions 之前收紧 session-list cursor 校验。REST / ACP HTTP 和 in-process ACP 现在会拒绝负数 cursor、仅包含空白的 cursor 字符串,以及超过 Number.MAX_SAFE_INTEGER 的值;省略 cursor 或传空字符串时,仍保持原有的首页行为。

这个改动保留非负小数 cursor 的合法性,因为 session 分页 cursor 来自文件系统 mtimeMs,它可能包含小数毫秒。测试现在覆盖非法边界,并断言小数 cursor 会原样转发、不被截断。

为什么需要

Session-list cursor 由 SessionService.listSessions() 基于文件 mtimeMs 生成。当前 handler 已经拒绝非数字和非有限 cursor,但仍会接受 -19007199254740992 这类有限数值。它们不是有效的分页 cursor:负数 cursor 可能产生空的持久化页面,unsafe 数值在过滤前可能发生精度丢失。

拒绝这些值可以让 REST / ACP HTTP 与 in-process ACP 路径保持一致,同时继续接受 service 实际可能返回的 cursor token。

审核测试计划

如何验证

运行:

npm test --workspace=packages/cli -- serve/server.test.ts -t cursor --coverage.enabled=false
npm test --workspace=packages/cli -- acp-integration/acpAgent.test.ts -t unstable_listSessions --coverage.enabled=false
npx prettier --check packages/cli/src/serve/server.ts packages/cli/src/serve/server.test.ts packages/cli/src/acp-integration/acpAgent.ts packages/cli/src/acp-integration/acpAgent.test.ts
npx eslint packages/cli/src/serve/server.ts packages/cli/src/serve/server.test.ts packages/cli/src/acp-integration/acpAgent.ts packages/cli/src/acp-integration/acpAgent.test.ts
npm run lint --workspace=packages/cli --if-present
git diff --check

预期行为:带有 abc-1Infinity9007199254740992 或仅空白字符串等非法 cursor 的 REST session-list 请求返回 400 invalid_cursor。In-process ACP 会在构造 SessionService 前拒绝相同的非法 cursor。1000123.456 这类非负小数 cursor 仍然合法,并且不会被截断。

已知的无关本地检查失败:

npm run typecheck --workspace=packages/cli --if-present

该命令仍然在 src/ui/components/BaseTextInput.tsx 失败,因为本地 TypeScript 无法解析 ink/domink/components/CursorContext;这些文件不在本 PR 范围内。

证据(修改前后)

N/A — 非 UI 的 API 校验变更。单元测试覆盖修改前后的行为:非法负数、unsafe、非有限、非数字和仅空白 cursor 会被拒绝;省略和空 cursor 仍然表示首页;合法的小数 mtimeMs cursor 会被保留。

测试平台

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

环境(可选)

本地 Node/npm workspace。我运行了 focused Vitest、Prettier check、targeted ESLint、workspace lint 和 git diff --check。CLI typecheck 存在上面提到的无关 ink/dom 解析失败。

风险与范围

  • 主要风险或取舍:发送格式错误但有限数值 cursor 的客户端,现在会收到校验错误,而不是空页面或精度不可靠的页面。service 返回的 cursor 值仍会被接受,包括小数 mtimeMs 值。
  • 未验证 / 不在范围内:改变 cursor token 格式,或在 package 间新增共享导出的 parser。
  • 破坏性变更 / 迁移说明:对有效客户端预计无影响。省略和空 cursor 会继续表示首页。

关联 Issue

Fixes #5708

AI 辅助披露

I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.

@tt-a1i
tt-a1i marked this pull request as ready for review June 22, 2026 20:37
@wenshao

wenshao commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR, @tt-a1i!

Template looks good ✓ — all required sections present with clear motivation and test plan.

On direction: this is a focused defensive fix for session-list cursor validation, directly addressing #5708. It fits squarely within roadmap/session-management and the issue carries the welcome-pr label. No concerns here.

On approach: the scope is tight — four files, only cursor validation touched, no drive-by changes. The fix extracts parse functions in both the REST (parseSessionCursor) and ACP (parseAcpSessionListCursor) paths, adding checks for negative values, whitespace-only strings, and values above Number.MAX_SAFE_INTEGER. The two near-identical functions use different error types (InvalidCursorError vs RequestError.invalidParams) to match their respective error-handling conventions, which is the pragmatic choice. A shared validator could trim ~8 lines of duplication but would require cross-module coupling for a small gain.

One minor observation: the ACP path calls value.trim() and then checks trimmed === '' — the second check is redundant after the first value === '' guard for whitespace-only input, but it's not incorrect, just belt-and-suspenders.

Moving on to code review and testing. 🔍

中文说明

感谢贡献,@tt-a1i

模板完整 ✓ — 所有必填章节齐全,动机和测试计划清晰。

方向:这是对 session-list cursor 校验的防御性修复,直接对应 #5708。完全在 roadmap/session-management 范围内,issue 也带有 welcome-pr 标签。没有方向上的顾虑。

方案:范围紧凑——仅涉及 4 个文件,只修改 cursor 校验逻辑,无夹带改动。在 REST(parseSessionCursor)和 ACP(parseAcpSessionListCursor)两条路径分别提取解析函数,新增对负数、纯空白和超过 Number.MAX_SAFE_INTEGER 的检查。两个近似相同的函数使用不同错误类型(InvalidCursorError vs RequestError.invalidParams)以匹配各自的错误处理惯例,这是务实的选择。共享校验器可以节省约 8 行重复代码,但需要跨模块耦合,收益不大。

一个小的观察:ACP 路径先调用 value.trim() 再检查 trimmed === '' ——在第一个 value === '' 判断之后,第二个检查对纯空白输入是多余的,但并不算错,只是双重保险。

进入代码审查和测试 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

The implementation is clean and focused. Two near-identical parse functions (parseSessionCursor in server.ts, parseAcpSessionListCursor in acpAgent.ts) share the same validation logic but throw different error types — InvalidCursorError for REST and RequestError.invalidParams for ACP. This is the pragmatic choice given the different error-handling conventions. No correctness bugs, security holes, or regressions found.

Unit Tests

All 14 cursor-specific tests pass (9 in server.test.ts, 5 in acpAgent.test.ts). The broader session test suite (39 tests) also passes with no regressions.

 ✓ src/serve/server.test.ts (512 tests | 503 skipped) 104ms
 ✓ src/acp-integration/acpAgent.test.ts (133 tests | 128 skipped) 267ms

Before (installed build — qwen 0.18.5)

Live HTTP test against qwen serve on port 14170:

=== Before: cursor=-1 ===
{"sessions":[]}
HTTP 200                    ← BUG: should be 400

=== Before: cursor=9007199254740992 ===
{"sessions":[]}
HTTP 200                    ← BUG: should be 400

=== Before: cursor=abc ===
{"error":"Invalid cursor: \"abc\" is not a valid numeric cursor","code":"invalid_cursor"}
HTTP 400                    ← correct

=== Before: cursor=1000123.456 (valid) ===
{"sessions":[]}
HTTP 200                    ← correct

After (PR #5709 — cursor validation logic)

npm run dev -- serve could not start in this CI environment due to an unrelated ink/dom export issue in BaseTextInput.tsx (pre-existing, not caused by this PR). Verified the PR's cursor parsing logic directly:

=== BEFORE (installed qwen 0.18.5) ===
  non-numeric ("abc"): ✓ REJECT
  negative ("-1"): ✗ ACCEPT (should reject) → -1
  Infinity ("Infinity"): ✓ REJECT
  above MAX_SAFE_INTEGER ("9007199254740992"): ✗ ACCEPT (should reject) → 9007199254740992
  whitespace-only ("   "): ✗ ACCEPT (should reject) → 0
  empty (first page) (""): ✓ ACCEPT → undefined
  valid fractional mtime ("1000123.456"): ✓ ACCEPT → 1000123.456
  valid integer mtime ("1797860000000"): ✓ ACCEPT → 1797860000000

=== AFTER (PR #5709) ===
  non-numeric ("abc"): ✓ REJECT
  negative ("-1"): ✓ REJECT
  Infinity ("Infinity"): ✓ REJECT
  above MAX_SAFE_INTEGER ("9007199254740992"): ✓ REJECT
  whitespace-only ("   "): ✓ REJECT
  empty (first page) (""): ✓ ACCEPT → undefined
  valid fractional mtime ("1000123.456"): ✓ ACCEPT → 1000123.456
  valid integer mtime ("1797860000000"): ✓ ACCEPT → 1797860000000

The PR's validation logic correctly rejects all three previously-accepted invalid cursor types (negative, unsafe integer, whitespace-only) while preserving valid fractional mtimeMs cursors.

中文说明

代码审查

实现干净且聚焦。两个近似相同的解析函数(server.ts 中的 parseSessionCursoracpAgent.ts 中的 parseAcpSessionListCursor)共享相同的校验逻辑但抛出不同错误类型——REST 用 InvalidCursorError,ACP 用 RequestError.invalidParams。鉴于不同的错误处理惯例,这是务实的选择。未发现正确性 bug、安全漏洞或回归。

单元测试

全部 14 个 cursor 相关测试通过(server.test.ts 中 9 个,acpAgent.test.ts 中 5 个)。更广泛的 session 测试套件(39 个测试)也无回归通过。

修改前(已安装版本 — qwen 0.18.5)

对 14170 端口的 qwen serve 进行 HTTP 实测:cursor=-1cursor=9007199254740992 返回 HTTP 200(应为 400)。cursor=abc 正确返回 400。

修改后(PR #5709

npm run dev -- serve 因环境中不相关的 ink/dom 导出问题无法启动(已有问题,非本 PR 引起)。直接验证了 PR 的 cursor 解析逻辑:所有三种之前错误接受的非法 cursor 类型(负数、unsafe 整数、纯空白)现在被正确拒绝,合法的 fractional mtimeMs cursor 保持接受。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

This is a clean, minimal defensive fix. The motivation is sound — three types of invalid cursor values (negative, unsafe integer, whitespace-only) slipped through the existing isFinite-only check and produced silently wrong results (empty pages or precision loss). The fix adds the right checks without changing any valid behavior.

The implementation is well-scoped: four files, no drive-by refactors, every line serves the stated goal. The ~8 lines of duplicated validation logic between the REST and ACP paths is the pragmatic trade-off — different error types, different modules, and extracting would add cross-module coupling for a tiny gain.

14 unit tests pass, covering all edge cases. The live HTTP test against the installed build confirmed the bug (cursor=-1 → HTTP 200), and the logic test confirmed the fix rejects all three invalid types while preserving valid fractional mtimeMs cursors. The npm run dev full-serve test couldn't run in this CI environment due to a pre-existing ink/dom export issue unrelated to this PR, but the unit test coverage is thorough enough.

Recommendation: approve. Ships cleanly, does what it says, no concerns.

中文说明

这是一个干净、最小化的防御性修复。动机合理——三类非法 cursor 值(负数、unsafe 整数、纯空白)在现有仅检查 isFinite 的逻辑下被错误接受,导致静默的错误结果(空页面或精度丢失)。修复新增了正确的校验,不改变任何合法行为。

实现范围合理:4 个文件,无夹带重构,每行代码都服务于目标。REST 和 ACP 路径之间约 8 行的重复校验逻辑是务实的取舍——不同错误类型、不同模块,提取共享函数会增加跨模块耦合,收益不大。

14 个单元测试全部通过,覆盖所有边界。HTTP 实测确认了 bug(cursor=-1 → HTTP 200),逻辑测试确认修复拒绝了全部三类非法值,同时保留合法的小数 mtimeMs cursor。npm run dev 完整 serve 测试因环境中已有的 ink/dom 导出问题无法运行,但单元测试覆盖已足够充分。

建议:通过。改动干净,实现了承诺,无顾虑。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao

wenshao commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

✅ Maintainer verification — real local test (tmux)

I verified this PR locally by booting the actual qwen serve daemon (separate process, real TCP socket) and curling the session-list endpoint, plus real-app / real-agent A/B tests on both changed paths — not just by reading the diff. Verdict: LGTM, safe to merge. The two paths (REST server.ts and in-process ACP acpAgent.ts) now reject -1, values > Number.MAX_SAFE_INTEGER, and whitespace-only cursors, while fractional mtimeMs cursors stay valid and untruncated.

Setup

  • Dedicated git worktree at PR head ebfe61358, with a real npm ci (no symlinked node_modules); all commands driven through a real tmux session, Node v22.22.2, Linux x64.
  • The CLI built cleanly here (the author's ink/dom failure is tsc --noEmit-only and doesn't affect tsc --build/dist).

Test 1 — Real daemon over a real socket (the headline)

Booted node packages/cli/dist/index.js serve --port 41709 --hostname 127.0.0.1 --workspace /tmp/ws5709 (loopback → auth-free) and curled GET /workspace/:id/sessions?cursor=…. Cursor validation runs before the data layer, so the status codes are conclusive without seeding any sessions:

cursor HTTP body
(omitted) 200 first page
'' (empty) 200 first page
0 200 page (valid boundary)
1000123.456 (fractional) 200 page — fractional mtimeMs accepted
1797860000000 200 page
abc 400 invalid_cursor (rejected before too)
Infinity 400 invalid_cursor (rejected before too)
-1 400 invalid_cursor — newly rejected
9007199254740992 (MAX_SAFE_INTEGER+1) 400 invalid_cursor — newly rejected
(whitespace-only) 400 invalid_cursor — newly rejected

Test 2 — REST path A/B (real Express app via supertest, server.test.ts -t cursor)

  • On the PR: 9/9 pass, incl. all five 400 invalid_cursor edges + accepts fractional mtime cursor values + passes fractional cursor values to SessionService without truncating.
  • Revert only the added reject clauses (back to !Number.isFinite only) → exactly the 3 new edges fail, the rest stay green:
    × 400 invalid_cursor … : -1                  → expected 200 to be 400
    × 400 invalid_cursor … : 9007199254740992    → expected 200 to be 400
    × 400 invalid_cursor … : '   '               → expected 200 to be 400
    ✓ abc, ✓ Infinity, ✓ accepts fractional, ✓ passes fractional without truncating
    Tests  3 failed | 6 passed
    
    Restoring the fix turns them green again. The surgical toggle proves the tests guard exactly what the PR adds — and that fractional precision is unaffected either way.

Test 3 — In-process ACP path A/B (real QwenAgent, acpAgent.test.ts -t unstable_listSessions)

  • On the PR: 5/5 pass, incl. rejects invalid cursors before listing sessions (loops abc, Infinity, -Infinity, -1, 9007199254740992, ' ') and passes a finite non-negative cursor through to SessionService (fractional 1797860000000.5).
  • Revert the ACP validator → the reject-loop test fails, and the failure is the bug itself: the invalid cursor is no longer rejected and instead flows down to the data layer:
    expected [Function] to throw 'Invalid cursor: "-1" …'
    but got 'sessionService.listSessions is not a function'
    
    i.e. -1 reached SessionService.listSessions instead of being rejected up front. Restoring the fix makes it green.

Notes (non-blocking)

  • The two paths are now aligned. REST (parseSessionCursor) and ACP (parseAcpSessionListCursor) apply identical rules, verified independently above — good for orchestrators that may hit either surface.
  • Whitespace was a real latent bug. Before the fix, cursor=" " coerced via Number(" ") → 0, silently serving page-at-0 instead of erroring; now it's a clean 400.
  • No precision regression. Fractional mtimeMs cursors (1000123.456, 1797860000000.5) pass through untruncated in both toggle states — a naive parseInt/Math.floor fix would have broken this; this PR correctly does not.
  • First-page semantics for omitted/empty cursors are preserved; 0 remains a valid boundary.

Conclusion: behavior verified end-to-end through the real daemon over a socket, both validation paths A/B-proven on the real app and real agent, with fractional-cursor precision intact and no regressions. 👍

🇨🇳 中文版(合并参考)

✅ 维护者验证 —— 本地真实测试(tmux)

我在本地通过启动真实的 qwen serve 守护进程(独立进程、真实 TCP socket)curl session-list 接口,并对两条改动路径(REST server.ts 和 in-process ACP acpAgent.ts)做了真实 app / 真实 agent 的 A/B 测试,而不只是看 diff。结论:LGTM,可以合并。 两条路径现在都会拒绝 -1> Number.MAX_SAFE_INTEGER 的值以及仅空白的 cursor,同时保留小数 mtimeMs cursor 的合法性且不截断。

环境

  • 在 PR head ebfe61358 上单独开了 git worktree,执行了真实 npm ci(没有软链 node_modules);所有命令都在真实 tmux 会话里跑,Node v22.22.2,Linux x64。
  • CLI 在这里能正常构建(作者提到的 ink/dom 失败只发生在 tsc --noEmit typecheck,不影响 tsc --build/dist)。

测试 1 —— 真实守护进程 + 真实 socket(重点)

启动 node packages/cli/dist/index.js serve --port 41709 --hostname 127.0.0.1 --workspace /tmp/ws5709(loopback → 免鉴权),然后 curl GET /workspace/:id/sessions?cursor=…。cursor 校验发生在数据层之前,所以不需要预置任何 session,状态码就足以定论:

cursor HTTP body
(省略) 200 首页
''(空) 200 首页
0 200 页(合法边界)
1000123.456(小数) 200 页 —— 小数 mtimeMs 被接受
1797860000000 200
abc 400 invalid_cursor(改之前也拒绝)
Infinity 400 invalid_cursor(改之前也拒绝)
-1 400 invalid_cursor —— 本 PR 新增拒绝
9007199254740992MAX_SAFE_INTEGER+1) 400 invalid_cursor —— 本 PR 新增拒绝
(仅空白) 400 invalid_cursor —— 本 PR 新增拒绝

测试 2 —— REST 路径 A/B(通过 supertest 跑真实 Express app,server.test.ts -t cursor

  • 在 PR 上:9/9 通过,包含全部五个 400 invalid_cursor 边界 + accepts fractional mtime cursor values + passes fractional cursor values to SessionService without truncating
  • 还原新增的拒绝条件(回到只有 !Number.isFinite)→ 恰好这 3 个新边界失败,其余保持绿色:
    × 400 invalid_cursor … : -1                  → expected 200 to be 400
    × 400 invalid_cursor … : 9007199254740992    → expected 200 to be 400
    × 400 invalid_cursor … : '   '               → expected 200 to be 400
    ✓ abc, ✓ Infinity, ✓ accepts fractional, ✓ passes fractional without truncating
    Tests  3 failed | 6 passed
    
    恢复修复后又变绿。这个外科手术式的 toggle 证明测试精确守住了 PR 新增的内容——且小数精度在两种状态下都不受影响。

测试 3 —— in-process ACP 路径 A/B(真实 QwenAgentacpAgent.test.ts -t unstable_listSessions

  • 在 PR 上:5/5 通过,包含 rejects invalid cursors before listing sessions(遍历 abc, Infinity, -Infinity, -1, 9007199254740992, ' ')和 passes a finite non-negative cursor through to SessionService(小数 1797860000000.5)。
  • 还原 ACP 校验器 → 该 reject-loop 测试失败,而失败本身就是 bug:非法 cursor 不再被拒绝,反而流到了数据层
    expected [Function] to throw 'Invalid cursor: "-1" …'
    but got 'sessionService.listSessions is not a function'
    
    也就是 -1 一路到了 SessionService.listSessions,而不是在入口处被拒绝。恢复修复后变绿。

备注(不阻塞合并)

  • 两条路径现在一致了。 REST(parseSessionCursor)和 ACP(parseAcpSessionListCursor)应用相同规则,上面分别独立验证——对可能命中任一入口的 orchestrator 是好事。
  • 空白曾是一个真实的潜在 bug。 修复前 cursor=" " 会经 Number(" ") → 0 静默地返回 page-at-0 而不是报错;现在是干净的 400
  • 没有精度回归。 小数 mtimeMs cursor(1000123.4561797860000000.5)在两种 toggle 状态下都原样透传、不截断——用 parseInt/Math.floor 的简单修法会破坏这一点,而本 PR 正确地没有这么做。
  • 省略/空 cursor 的首页语义保持不变;0 仍是合法边界。

结论: 通过真实守护进程 + socket 端到端验证了行为,两条校验路径都在真实 app 和真实 agent 上做了 A/B 证明,小数 cursor 精度完好且无回归。👍

@wenshao
wenshao merged commit abec0a5 into QwenLM:main Jun 23, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(cli): session list cursor accepts negative and unsafe values

3 participants