Skip to content

refactor: convert sendAIMessage from recursion to iterative while-loop#1337

Merged
lewis617 merged 1 commit into
mainfrom
pr-1319-rebase
Jul 3, 2026
Merged

refactor: convert sendAIMessage from recursion to iterative while-loop#1337
lewis617 merged 1 commit into
mainfrom
pr-1319-rebase

Conversation

@lewis617

@lewis617 lewis617 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Convert the recursive sendAIMessage method in aiManager.ts to an iterative nested while-loop design, eliminating stack growth that caused OOM at depth 2418+.

Changes

  • Nested while-loops: outer loop handles restarts (notifications, goal continuation, stop hooks); inner loop handles tool-call continuation
  • turnOffset (= initial recursionDepth) gates depth-0 init and finally-equivalent cleanup — same semantics as the old recursionDepth === 0 checks
  • turnDepth (= turnOffset + inner loop counter) replaces recursionDepth in maxTurns check
  • Removed setImmediate hack for stack growth (PR fix: yield event loop before sendAIMessage recursion to prevent OOM #1325) — while-loop doesn't grow stack
  • Kept setImmediate yield before inner continue — needed so macrotasks (abort timers) can fire between turns in mocked test scenarios where all async ops are microtasks
  • try/catch/finally became try/catch inside inner loop + sequential finally-equivalent code after it
  • All 4 recursive call sites converted:
    • Tool-call continuation → turnDepth++; continue inner;
    • Notification/goal/stop-hook restarts → shouldRestart=true; turnOffset=0
  • Renamed agent.toolRecursion.test.tsagent.toolLoop.test.ts

What stays the same

  • Method signature unchanged (recursionDepth param kept for backward compat)
  • All external callers unchanged
  • Error handling, saveSession counts, OTel spans, loading state, maxTurns enforcement

Verification

  • All targeted test files pass (85 tests)
  • Full agent-sdk suite: 3151 passed, 1 skipped (235 files)
  • Coverage tests (original OOM scenario): pass
  • Type-check (all 3 packages): clean

Closes #1324

Replace recursive sendAIMessage with nested outer/inner while-loops:
- outer loop handles restarts (notifications, goal continuation, stop hooks)
- inner loop handles tool-call continuation
- turnOffset gates depth-0 init and finally-equivalent cleanup
- turnDepth replaces recursionDepth in maxTurns check

Keep setImmediate yield before inner continue to let macrotasks (abort
timers) fire between turns in mocked test scenarios.

Rename agent.toolRecursion.test.ts → agent.toolLoop.test.ts.

Closes #1324
@lewis617 lewis617 enabled auto-merge (rebase) July 3, 2026 04:25
@lewis617 lewis617 merged commit 94292e8 into main Jul 3, 2026
1 check passed
@lewis617 lewis617 deleted the pr-1319-rebase branch July 3, 2026 04:28
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.

refactor: sendAIMessage 递归改为迭代循环

1 participant