Skip to content

fix(runtime): reliable, correlation-safe, non-blocking elicitation delivery (#3584) - #3587

Merged
dgageot merged 1 commit into
mainfrom
fix/a6-elicitation-concurrency
Jul 15, 2026
Merged

dgageot merged 1 commit into
mainfrom
fix/a6-elicitation-concurrency

Conversation

@aheritier

@aheritier aheritier commented Jul 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Previously this PR was the full A6 implementation; it has been split into a reviewable stack and now scopes to the runtime core only. Fixes the heart of #3584 — elicitation requests raised from concurrent background jobs were silently dropped and the requesting handler blocked forever.

#3584 stack

  1. ✅ fix(tui): queue pending attention events FIFO instead of a single slot (#3584) #3618 — fix(tui): FIFO pending-attention queue (merged)
  2. this PR (fix(runtime): reliable, correlation-safe, non-blocking elicitation delivery (#3584) #3587) — runtime core: correlation, delivery, API field
  3. feat(tui,app): surface & correlate background-job elicitations (#3584) #3624 — feat(tui,app): surface & correlate background-job elicitations
  4. feat(server): session-scoped elicitation sink for API/server runtimes (#3584) #3625 — feat(server): session-scoped elicitation sink

What changed

  • Per-request correlation: waiters keyed by an internal ID, registered before the request event is emitted (eliminates the TOCTOU branch).
  • Reliable delivery: the OnElicitationRequest sink is called synchronously; the best-effort bridge send is ctx-bounded on a detached goroutine and can never block delivery or response processing.
  • Atomic resolve-vs-cancel: a terminal-state guard ensures a delivered response is never discarded by a racing ctx.Done().
  • Backward-compatible API: ResumeElicitation's new elicitationID is variadic (pre-Concurrent background-job elicitations are silently dropped — nothing displayed to the user #3584 callers still compile); additive elicitation_id wire field; server pass-through wires it end-to-end.
  • Headless fast-decline with a model-readable note when no sink is registered.

The Runtime interface change and its repo-wide implementor/mock updates are one commit for Go compile atomicity.

Testing

  • pkg/runtime/elicitation_concurrency_test.go: duplicate wire IDs, cancel-vs-resolve race (300-iter -race), wedged bridge, real-handler TOCTOU, exactly-once delivery, concurrent background-job routing.
  • Full stack task dev + -race — green.

Bottom of the remaining stack (targets main now that #3618 is merged).


For advanced use (custom elicitation, raw event inspection), call `chat.Runtime()` to access the underlying `runtime.Runtime` directly.

> [!WARNING]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

can it impact AP @simonferquel ?

b.ch <- ev
return nil
select {
case b.ch <- ev:
// means the response will reach the caller, never a discarded one.
func (r *LocalRuntime) ResumeElicitation(ctx context.Context, action tools.ElicitationAction, content map[string]any, elicitationID ...string) error {
id := firstElicitationID(elicitationID)
slog.DebugContext(ctx, "Resuming runtime with elicitation response", "agent", r.currentAgentName(), "action", action, "elicitation_id", id)
ok = r.elicitationWaiters.resolveSingle(result)
}
if !ok {
slog.DebugContext(ctx, "No matching elicitation request in progress", "elicitation_id", id)
slog.DebugContext(ctx, "No matching elicitation request in progress", "elicitation_id", id)
return errNoSuchElicitation
}
slog.DebugContext(ctx, "Elicitation response sent successfully", "action", action)
func (r *RemoteRuntime) ResumeElicitation(ctx context.Context, action tools.ElicitationAction, content map[string]any) error {
slog.DebugContext(ctx, "Resuming remote runtime with elicitation response", "agent", r.currentAgent, "action", action, "session_id", r.sessionID)
func (r *RemoteRuntime) ResumeElicitation(ctx context.Context, action tools.ElicitationAction, content map[string]any, elicitationID ...string) error {
id := firstElicitationID(elicitationID)
slog.DebugContext(ctx, "Resuming remote runtime with elicitation response", "agent", r.currentAgent, "action", action, "session_id", r.sessionID)
func (r *RemoteRuntime) ResumeElicitation(ctx context.Context, action tools.ElicitationAction, content map[string]any, elicitationID ...string) error {
id := firstElicitationID(elicitationID)
slog.DebugContext(ctx, "Resuming remote runtime with elicitation response", "agent", r.currentAgent, "action", action, "session_id", r.sessionID, "elicitation_id", id)
@aheritier aheritier added area/api For features/issues/fixes related to the usage of the cagent API area/cli CLI commands, flags, output formatting area/docs Documentation changes area/runtime Runtime engine, agent loop execution, tool dispatch, loop detection area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix:). Use on PRs only. labels Jul 11, 2026
@aheritier
aheritier force-pushed the fix/a6-elicitation-concurrency branch 2 times, most recently from 06d22e7 to 5db8299 Compare July 13, 2026 14:37
@aheritier
aheritier force-pushed the fix/a6-elicitation-concurrency branch from 5db8299 to 811d91d Compare July 13, 2026 14:40
@aheritier aheritier changed the title fix(runtime): reliably surface & correlate concurrent elicitation requests (#3584) fix(runtime): reliable, correlation-safe, non-blocking elicitation delivery (#3584) Jul 13, 2026
@aheritier
aheritier marked this pull request as ready for review July 13, 2026 14:56
@aheritier
aheritier requested a review from a team as a code owner July 13, 2026 14:56
@aheritier

Copy link
Copy Markdown
Collaborator Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

👋 Heads up: The /review command is deprecated. Please re-request a review from docker-agent in the PR sidebar instead.

@aheritier
aheritier requested a review from docker-agent July 15, 2026 09:25
@dgageot
dgageot merged commit 3556547 into main Jul 15, 2026
25 of 29 checks passed
@dgageot
dgageot deleted the fix/a6-elicitation-concurrency branch July 15, 2026 14:42
aheritier added a commit that referenced this pull request Jul 15, 2026
…ibility (#3584)

Change NewElicitationDialog, NewURLElicitationDialog,
NewOAuthAuthorizationDialog, and App.ResumeElicitation to take the new
correlation-ID argument as a trailing variadic elicitationID ...string
instead of a required positional string, following #3587's precedent for
Runtime.ResumeElicitation.

This restores source compatibility for external callers and fixes the
compile break against 3-arg NewElicitationDialog call sites added on main
(bebfc22). Existing call sites in this branch that pass a real
elicitation ID are unchanged (still pass it as the variadic arg), so
response correlation is preserved.

NewOAuthAuthorizationDialog's parameter order also changes: appInstance now
comes before the new variadic elicitationID, so the trailing ID stays last
across all four signatures.
aheritier added a commit that referenced this pull request Jul 15, 2026
…ibility (#3584)

Change NewElicitationDialog, NewURLElicitationDialog,
NewOAuthAuthorizationDialog, and App.ResumeElicitation to take the new
correlation-ID argument as a trailing variadic elicitationID ...string
instead of a required positional string, following #3587's precedent for
Runtime.ResumeElicitation.

This restores source compatibility for external callers and fixes the
compile break against 3-arg NewElicitationDialog call sites added on main
(bebfc22). Existing call sites in this branch that pass a real
elicitation ID are unchanged (still pass it as the variadic arg), so
response correlation is preserved.

NewOAuthAuthorizationDialog's parameter order also changes: appInstance now
comes before the new variadic elicitationID, so the trailing ID stays last
across all four signatures.
social4hyq pushed a commit to social4hyq/homebrew-core that referenced this pull request Sep 20, 2026
docker-agent 1.110.0

Created-by: HarmonybrewBot
Commit-by: HarmonybrewBot
Merged-by: HarmonybrewBot
Description: Created by `brew bump`

---

Created with `brew bump-formula-pr`.<details>
  <summary>release notes</summary>
  <pre>This release adds dynamic MCP HTTP headers, new shell safety policies, per-model custom pricing, and cache-stable dynamic prompts, along with fixes for elicitation delivery and session cost tracking.

## What's New

- Adds dynamic MCP HTTP headers via a header factory, enabling context-aware HTTP header resolution instead of static headers fixed at startup
- Adds a `safe-auto` safety policy for shell operations, providing a middle ground between prompting for every tool call and `--yolo` mode; safe calls beyond shell are auto-approved under this policy, with an opt-in `safer` mode available via `approve-safer` resume
- Adds an optional `cost` block to model configuration, allowing explicit per-model token pricing for custom, locally-hosted, or uncatalogued models
- Adds opt-in cache-stable dynamic prompts (`cache_stable_prompts` user setting), persisting frozen instruction snapshots and appending chronological updates when trusted context changes

## Bug Fixes

- Fixes elicitation requests from concurrent background jobs being silently dropped and blocking the requesting handler indefinitely
- Fixes session cost dropping after compaction (e.g., from $49 to $12) by keeping cost monotonic across compaction and preserving cost accounting across reload
---

## What's Changed
* feat: implement dynamic MCP HTTP headers via header factory by @Piyush0049 in docker/docker-agent#3583
* fix(runtime): reliable, correlation-safe, non-blocking elicitation delivery (#3584) by @aheritier in docker/docker-agent#3587
* docs: update CHANGELOG.md for v1.109.0 by @docker-read-write[bot] in docker/docker-agent#3661
* Add opt-in cache-stable dynamic prompts by @rumpl in docker/docker-agent#3662
* fix(session): keep cost monotonic across compaction by @Sayt-0 in docker/docker-agent#3663
* feat(safer_shell): add safe-auto safety policy by @trungutt in docker/docker-agent#3647
* feat(config): declare explicit per-model token pricing with cost by @dgageot in docker/docker-agent#3664


**Full Changelog**: docker/docker-agent@v1.109.0...v1.110.0
</pre>
  <p>View the full release notes at <a href="https://github.com/docker/docker-agent/releases/tag/v1.110.0">https://github.com/docker/docker-agent/releases/tag/v1.110.0</a>.</p>
</details>
<hr>

See merge request: Harmonybrew/homebrew-core!14319
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api For features/issues/fixes related to the usage of the cagent API area/cli CLI commands, flags, output formatting area/docs Documentation changes area/runtime Runtime engine, agent loop execution, tool dispatch, loop detection area/tui For features/issues/fixes related to the TUI kind/fix PR fixes a bug (maps to fix:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants