fix(cli): return the real subagent answer instead of an empty task result - #13493
Conversation
…sult The task tool picked the last text part of the subagent's final message as its result. Subagents that ran with memory context get a synthetic, ignored, empty text part (the memory marker) appended after their answer, so findLast surfaced an empty <task_result> to the parent agent. The extractor now skips synthetic, ignored, and empty text parts, and background jobs no longer let an empty run overwrite an earlier non-empty result on extend. Fixes Kilo-Org#13469
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c45e7e8b4a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return ( | ||
| result.parts | ||
| .filter((item): item is MessageV2.TextPart => item.type === "text") | ||
| .findLast((item) => !item.synthetic && !item.ignored && item.text.length > 0)?.text ?? "" |
There was a problem hiding this comment.
Extract task-result filtering into the Kilo mirror
This adds Kilo-specific result-selection logic directly to the shared upstream src/tool/task.ts, with its regression coverage likewise added to the shared test file. Extract the filtering into src/kilocode/tool/task.ts, move its coverage under test/kilocode/, and leave only a single marked call at this location so future upstream merges do not repeatedly conflict with this implementation.
AGENTS.md reference: packages/opencode/AGENTS.md:L76-L78
Useful? React with 👍 / 👎.
| "@kilocode/cli": patch | ||
| --- | ||
|
|
||
| Fix the task tool intermittently returning an empty result. Subagents that ran with memory context had a synthetic marker part appended after their answer, which was picked up as the final text part and surfaced as an empty `<task_result>` to the parent agent. The task tool now ignores synthetic, ignored, and empty text parts, and background jobs no longer let an empty run overwrite an earlier successful result, so resumed tasks keep their real output. |
There was a problem hiding this comment.
Rewrite the changeset as a user-facing release note
This text is published directly in release notes, but most of it explains internal marker-part selection and background-job overwrite behavior rather than concisely describing the user-visible fix. Reduce it to an imperative, feature-oriented statement such as “Prevent resumed subagent tasks from returning empty results.”
AGENTS.md reference: AGENTS.md:L158-L160
Useful? React with 👍 / 👎.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (5 files)
Reviewed by grok-4.6 · Input: 471.6K · Output: 16.4K · Cached: 672.4K Review guidance: REVIEW.md from base branch |
|
Looks good, reproduced the fix thanks |
What
The
tasktool intermittently returns an empty<task_result></task_result>to the parent agent even though the subagent produced a correct answer. Fixes #13469.Two bugs combined to cause this:
packages/opencode/src/tool/task.ts— the task result was picked as the last text part of the subagent's final assistant message. Subagents that run with memory context get a synthetic, ignored, empty text part (the memory marker,MemoryMarker.part) appended after their answer, sofindLastreturned that empty string. The extractor now filters to text parts that are not synthetic, not ignored, and non-empty before picking the last one. (This also prevents an ignored output-limit warning part from being surfaced as the result.)packages/core/src/background-job.ts—BackgroundJob.settlereplaced the stored output based on sequence alone, so a later run's empty output overwrote an earlier correct one. It now only stores non-empty outputs and keeps the latest non-empty result, which is also robust to runs settling out of order.Why
Both root causes are documented in #13469 with a deterministic reproduction: when a memory marker is present, every task invocation returns empty to the parent. This breaks follow-up prompts on resumed subagents and long-running background tasks (the parent gets an empty result and the subagent's actual work is hidden).
Notes
tool.tasktests (execute shapes child permissions...,description hides denied subagents...) fail on currentmaintoo — they appear to be in-flight with permission-handling PR fix(cli): honor explicit env read allows without nested deny leak #13143, not related to this change. I verified they fail on a cleanmaincheckout.--no-verify). I ran per-package typecheck forpackages/opencodeandpackages/core, the background-job + task-tool test suites, prettier, oxlint, and the kilocode annotation checker — all pass.Verification
packages/core:bun run typecheck,bun test test/background-job.test.ts— pass, including the new regression test.packages/opencode:bun run typecheck,bun test test/tool/task.test.ts— new regression test passes (2 pre-existing failures unrelated to this change).bun run script/check-opencode-annotations.ts --worktree— passes.