Skip to content

fix(session): preserve recovery after empty stops and surface request-phase retries - #2618

Merged
wqymi merged 15 commits into
mainfrom
fix/resume-admission-parity
Oct 13, 2026
Merged

wqymi merged 15 commits into
mainfrom
fix/resume-admission-parity

Conversation

@wqymi

@wqymi wqymi commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Session recovery, Stop correctness, and visible request-phase retries:

  • Keep interrupted turns recoverable without replaying completed tools
  • Admit the exact reviewed resume target before history changes
  • Persist safe resume boundaries (planResumeTrim + real SDK conversion)
  • Isolate shared instance initialization from turn cancellation
  • Settle async tools on Stop: abort then consume the real result before releasing
  • Publish request-phase session.status{retry} through processor-owned setRetry so clients can show the reconnect row instead of a dead spinner during long server/network waits
  • Stop later busy frames from clobbering an in-flight retry status

Why request-phase status is back

11093c90 made request-ladder attempts diagnostics-only (RetryAttempt) to avoid stacked reconnect frames. Recoverable kinds then became the only waiter users observe (processor stream retries never see those failures), so the UI sat on a spinner with no explanation. Request ladders now write the same SessionStatus map that GET /session/status serves.

Verification

  • tool-gate-cancel / message-v2 / instance-state: 109 passed, 0 failed
  • processor-effect publish retry status updates: states [1, 2]
  • status-retry-map: setRetry is visible to list() / GET /session/status
  • retry-status-density-no-maxmode / retry-density-invalid-baseurl
  • Covers Stop waiting for plugin/exec settlement, tool input boundary (partial vs complete-empty), cold ToolRegistry cancel, resume not repeating file writes

Scope

  • No DB migration
  • ToolPart gains optional execution: not-started | started in the part JSON (no schema change)

Branch has been rebased onto main.

@wqymi wqymi changed the title fix(session): preserve recovery after empty stops and question cancellation fix(session): preserve recovery after empty stops and surface request-phase retries Oct 13, 2026
wqymi and others added 13 commits October 14, 2026 03:09
- settlement.ts:abort 后仍 consume 真实结果再抛中断
- processor executeTool/toolScope:Stop 关准入、等 Fiber 真退出
- 插件/脚本/沙箱工具接入可中断执行与结果登记
- tool-gate-cancel / processor-effect 覆盖结算、边界与冷启动取消
…efore model resolve

- planResumeTrim keeps delivered/protected output and empty containers
- drop file-only residue before the protected-output gate
- user-resume with no tools keeps delivered siblings, discards incomplete tails
- restore parent-scoped cleanupEmptyResidueAssistants before getModel
- align admission classify with planResume (tool-resume only with real tools)
- allow empty model input for user-resume (parent user may have no parts)
…esidue

- user-resume cleans sibling empty residue, keeps the resume target (TP-SR-R21-27)
- empty-stop matrix: empty target stays, placeholders/text tails still trimmed
- resume-empty-residue asserts durable empty containers per D16.3
- question isolation: handle p1/p2 rejections immediately (no unhandled rejection)
- observeWake waits on running/idle status instead of ActorExecution.current
- continuation second result uses textMatch so title generation cannot steal it
Title-generation probes were stealing the queued 401 and letting the
continuation succeed. Use pushMatch like the success path.
… is visible

Request-ladder retries were diagnostics-only after 11093c9, so long
server/network waits left Desktop on a dead spinner. Write SessionStatus
from the request ladder via processor-owned setRetry (same map GET
/session/status serves), and stop later busy frames from clobbering it.

Co-Authored-By: MiMo <noreply@xiaomi.com>
@wqymi
wqymi force-pushed the fix/resume-admission-parity branch from 1f2482c to 85c85e0 Compare October 13, 2026 19:22
wqymi added 2 commits October 14, 2026 03:31
The density test still expected busy during request ladders; that is the
old ownership. Request-phase now writes session.status{retry} so Desktop
can show the reconnect row.
…ent test

The scripted model calls example_probe directly; tool-search deferral
rejects it before tool.execute.after, so the settlement hook never runs.
@wqymi
wqymi merged commit 002cb4c into main Oct 13, 2026
6 checks passed
onlyfeng added a commit to onlyfeng/MiMo-Code that referenced this pull request Oct 14, 2026
Adopt upstream XiaomiMiMo#2618 (empty-stop recovery, request-phase retry status,
settled tool execution boundaries) onto the fork's owners:

- Tool execution boundary: ToolPart.execution / inputComplete, transactional
  Session.mutateToolPart, projector and import identity guards, processor
  executeTool with a closable tool scope, and ToolSettlement for plugin, MCP,
  registry and exec results. MCP output keeps harness-tag escaping and
  presentation metadata; failed tool metadata keeps the record guard.
- Exec (FD-006): the fork's nested close-abort-join lifecycle stays; the
  script settles through ToolSettlement and records a cancelled result, and
  a nested guest that settled while Stop interrupted it reports its real
  result. sandbox.ts returns to upstream bytes (equivalent signal abort).
- Resume (FC-001): settlement and continuation stay; upstream's history trim
  is not adopted. Adopt exact assistantMessageID targets (also for actor
  resume), refusal while a question or permission is pending, the host
  execution attempt (prepare, admit before settlement, beforeWork, finish on
  run exit, ExecutionScope on model calls), admission outside the orphan
  sweep so an unadmitted run writes nothing, and 404 for a Stop before
  admission (mapped at the /resume route). An empty provider stop stays a candidate after completion or a
  later error until abandoned; summary/structured assistants are not residue.
- Runner/InstanceState: owned runs expose awaitExit; shared initialization
  is forked per generation and survives caller cancellation. A detached
  Bus.publish interrupted by instance disposal is a no-op, so it cannot
  surface as an unhandled rejection.
- Actor cancel from a model tool waits like a direct execution hook: the
  tool now runs in the processor's tool scope, which execution cleanup joins.
- Question: upstream's abortSignal input replaces the second argument, with
  the fork's closed-generation admission and dismiss; a reply in flight wins.
- Retry (FC-013): request-phase retries publish session.status{retry} for
  main only; busy no longer clobbers a retry row.

SDK regenerated from the merged routes.
onlyfeng added a commit to onlyfeng/MiMo-Code that referenced this pull request Oct 14, 2026
Eleven capabilities from upstream XiaomiMiMo#2618: the settled tool execution
boundary, request-phase retry status, Deferred InstanceState and owned-run
awaitExit adopted; exact-target resume, pending-input refusal, the host
execution attempt and empty-stop recovery adapted into FC-001 without
upstream's history trim; FD-006 exec close-abort-join and FD-005 MCP
harness-tag escaping kept. Records final-tree validation.
onlyfeng added a commit to onlyfeng/MiMo-Code that referenced this pull request Oct 14, 2026
Inherit main 772adc2 (upstream XiaomiMiMo#2618 adapted to FC-001, FD-006, FC-007,
FC-008, FC-009 and FC-013). One conflict in session/message-v2.ts: main's
planResumeTrim and assertResumeModelBoundary sit before DC-CONTEXT-001's
renamed internal converter, which keeps its name and caps; upstream's
structural-only assistant skip lands inside it. SDK regeneration from the
merged sources leaves packages/sdk/src unchanged.

Compat test adaptation: tool-gate-cancel's new settlement, exec and
input-boundary cases use a 128000-token test model, like the file's
existing case, so DC-CONTEXT-001 request preflight admits the complete
tool prefix.
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.

1 participant