Repository navigation
Conversation
… to the inbox The Plan plugin's context hook re-injects its reminder whenever compaction or a committed revert strips it, persisting the copy through ctx.session.synthetic. That admits a pending steer item, so the next idle boundary promotes it into a fresh turn answering the reminder again. Once auto-compaction keeps firing (small context limit), each turn re-strips and re-admits the reminder and 'opencode run --agent plan' never exits. Publish the reminder straight onto the durable log instead (the same path instruction injection uses): it lands in history without creating pending input, so reconciliation no longer spawns answer turns. Fixes anomalyco#62628
There was a problem hiding this comment.
The change works. I ran the #62628 mock-model repro from source. On the base, opencode run --agent plan with a 3000-token context was still looping when the timeout killed it after 40 s and about 4,000 requests. With this PR it exits after 3 requests (title, compaction, answer). Because this hook also handles the first request under default_agent: "plan", the same change stops the extra unprompted turn from #58287: 2 requests instead of 3, and the answer is printed once. plan.test.ts passes and tsc -b is clean for packages/core.
Public API: unchanged. This is an internal change to the built-in Plan plugin; no plugin API, events, config or HTTP surface is touched.
Publishing SessionEvent.Synthetic directly matches what instruction loading and the runner's continuation messages already do, and built-in plugins already depend on Bus.Service. No blocking concerns. One note for maintainers: #58395 is open and fixes #58287 a different way, by adding a prompt-path hook in plan.ts. With this PR that hook may no longer be needed, so the two should be reconciled.
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Issue for this PR
Closes #62628
Type of change
What does this PR do?
With the
planagent, once auto-compaction triggers, the session loops forever andopencode run --agent plannever exits.Root cause: the Plan plugin's context hook re-injects the plan-mode reminder whenever compaction (or a committed revert) strips it from history, and persists the copy through
ctx.session.synthetic. That admits a pending steer item to the session inbox, and at the next idle boundary the inbox promotes it into a fresh turn whose only job is answering the reminder. With a small enough context limit every answer triggers another compaction, so the cycle sustains itself.Fix: publish the reminder straight to the durable event log (
SessionEvent.Synthetic), the same path instruction-file injection already uses, instead of admitting it as pending inbox input. The reminder still lands in history and is still spliced into the current request's messages, but it no longer spawns an answer turn, so the loop cannot sustain itself. The agent-switch path (session.created/session.agent.selected) still goes throughctx.session.synthetic, so reminder delivery on agent switches is unchanged.Overlap with #58287 / #58395: #58287 (extra unprompted turn under
default_agent: "plan") shares the same mechanism - a synthetic reminder is admitted as pending input and promoted into an unprompted turn - so this change also stops it (verified locally: withdefault_agent: "plan"and no compaction, 3 requests ondevvs 2 with this PR). #58395 was opened earlier and fixes #58287 a different way; the auto-compaction loop in #62628 is specific to this PR. Happy to reconcile whichever way maintainers prefer.How did you verify your code works?
planagent with a tiny context limit: before, the process was killed after 40s / ~2,000 requests; after, it exits cleanly after 3 requests (title, compaction, answer).buildagent is unchanged (3 requests).default_agent: "plan"config, no--agentflag, large context: 3 requests ondev(extra unprompted answer turn), 2 with this PR.bun test test/plugin/plan.test.ts- 12/12 pass (updated: the context hook now publishes to the durable log rather than admitting to the inbox).bun run typecheck(packages/core) - clean.Checklist
Added by the opencode bot (AI-generated).
Also addresses #58287 (see overlap note above; verified locally 3 -> 2 requests)
Related: #57013