Skip to content

refactor(core): introduce InjectionService with source-aware injection and backend-native background completions#22544

Merged
mattKorwel merged 7 commits intomainfrom
afw/injection-service-infra
Mar 16, 2026
Merged

refactor(core): introduce InjectionService with source-aware injection and backend-native background completions#22544
mattKorwel merged 7 commits intomainfrom
afw/injection-service-infra

Conversation

@adamfweidman
Copy link
Copy Markdown
Contributor

@adamfweidman adamfweidman commented Mar 15, 2026

Description

PR 1 of 4 in the background task support chain (afw/injection-service-infraafw/execute-optionsafw/agnostic-background-uiafw/remote-agent-backgrounding).

Replaces UserHintService with a unified InjectionService that supports typed injection sources ('user_steering' | 'background_completion'). User steering injections are gated on the model steering toggle; background completions always fire.

Key architectural changes:

  • Source-aware getters: getInjections(source?) and getInjectionsAfter(index, source?) filter by source so background completions don't get formatted as user hints
  • Backend-native injection: ExecutionLifecycleService.setInjectionService() wired in Config constructor so settleExecution injects directly — no UI hop needed
  • Agent loop consumption: local-executor uses onInjection with source-aware queues (pendingHintsQueue for steering, pendingBgCompletionsQueue for background), draining both between turns
  • Correct ordering: Background context appears before user hints in the message so the model sees context before the user's reaction

Issues

Relates to #18197

Changes

  • Renamed UserHintServiceInjectionService with unified addInjection(text, source) / onInjection / offInjection API
  • Added FormatInjectionFn and BackgroundCompletionInfo to ExecutionLifecycleService for background completion lifecycle
  • Moved injection wiring from AppContainer useEffect to backend (Config constructor + settleExecution)
  • Added source filtering to indexed getters; removed dead getLastUserHintAt()
  • Minimal AppContainer diff: only userHintServiceinjectionService renames and source-filtering listener

Test plan

  • injectionService.test.ts — 10 tests (source filtering, gating, listeners, clear)
  • subagent-tool.test.ts — 15 tests (user hints with source-aware getters)
  • Lint and format clean across all 4 worktrees

…ckground completion support

Rename UserHintService to InjectionService as a generic, source-agnostic
injection mechanism. InjectionService supports typed sources ('user_steering'
and 'background_completion') with source-specific gating — user_steering
respects the model steering toggle while background_completion always fires.

Add background completion lifecycle to ExecutionLifecycleService: tracks
backgrounded executions, fires onBackgroundComplete listeners when they
settle, and supports FormatInjectionFn callbacks so execution creators
control how their output is formatted for reinjection.

Wire AppContainer to route background completions through InjectionService
and submit them to the model when idle, independent of model steering.
@adamfweidman adamfweidman requested a review from a team as a code owner March 15, 2026 15:12
@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli bot commented Mar 15, 2026

Hi there! Thank you for your contribution to Gemini CLI.

To improve our contribution process and better track changes, we now require all pull requests to be associated with an existing issue, as announced in our recent discussion and as detailed in our CONTRIBUTING.md.

This pull request is being closed because it is not currently linked to an issue. Once you have updated the description of this PR to link an issue (e.g., by adding Fixes #123 or Related to #123), it will be automatically reopened.

How to link an issue:
Add a keyword followed by the issue number (e.g., Fixes #123) in the description of your pull request. For more details on supported keywords and how linking works, please refer to the GitHub Documentation on linking pull requests to issues.

Thank you for your understanding and for being a part of our community!

@gemini-cli gemini-cli bot closed this Mar 15, 2026
@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli bot commented Mar 15, 2026

Hi @adamfweidman, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.

We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: Improving Our Contribution Process & Introducing New Guidelines.

Key Update: Starting January 26, 2026, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.

Thank you for your understanding and for being a part of our community!

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant refactoring of the content injection mechanism within the application. By generalizing the UserHintService into an InjectionService, the system can now handle various types of content injection, not just user hints. A major new capability is the integration of background execution completions, allowing long-running tasks to seamlessly feed their results back into the model conversation. This enhances the application's ability to manage and respond to asynchronous operations, improving overall responsiveness and user experience.

Highlights

  • Service Renaming: The UserHintService has been refactored and renamed to InjectionService to provide a more generic and source-agnostic mechanism for injecting content into the model conversation.
  • Background Completion Support: The ExecutionLifecycleService now includes support for tracking backgrounded executions and firing onBackgroundComplete listeners when these executions settle. This includes custom formatting for reinjection output.
  • AppContainer Integration: The AppContainer has been updated to route background completions through the new InjectionService and submit them to the model when idle, operating independently of the model steering toggle.
  • Typed Injection Mechanism: The new InjectionService supports typed sources ('user_steering' and 'background_completion'), allowing for source-specific gating, such as respecting the model steering toggle for user hints while always firing for background completions.
Changelog
  • packages/cli/src/test-utils/AppRig.tsx
    • Updated addUserHint to use the new injectionService instead of userHintService.
  • packages/cli/src/ui/AppContainer.tsx
    • Imported ExecutionLifecycleService, BackgroundCompletionInfo, and InjectionSource types.
    • Added pendingBackgroundCompletionsRef and pendingBgCompletionCount state variables for tracking background completions.
    • Implemented consumePendingBackgroundCompletions callback to process and clear background completion outputs.
    • Refactored the useEffect hook for hints to use injectionService.onInjection and differentiate between user_steering and background_completion sources.
    • Added a new useEffect hook to wire ExecutionLifecycleService.onBackgroundComplete events into the injectionService, formatting output for reinjection.
    • Updated addUserHint call to use config.injectionService.addUserHint.
    • Added a new useEffect hook to reinject completed background execution output into the model conversation, independent of model steering.
  • packages/cli/src/ui/commands/clearCommand.test.ts
    • Updated test configuration to use injectionService instead of userHintService.
  • packages/cli/src/ui/commands/clearCommand.ts
    • Updated clear call to use config?.injectionService.clear().
  • packages/core/src/agents/local-executor.test.ts
    • Updated test cases to use configWithHints.injectionService.addUserHint.
  • packages/core/src/agents/local-executor.ts
    • Replaced userHintService with injectionService for getting hint index, registering listeners, and retrieving hints.
  • packages/core/src/agents/subagent-tool.test.ts
    • Updated test cases to use mockConfig.injectionService.addUserHint.
  • packages/core/src/agents/subagent-tool.ts
    • Replaced userHintService with injectionService for getting hint index and retrieving hints.
  • packages/core/src/config/config.ts
    • Updated import from userHintService.js to injectionService.js.
    • Renamed userHintService property to injectionService.
    • Instantiated injectionService using InjectionService instead of UserHintService.
  • packages/core/src/config/injectionService.test.ts
    • Added a new test file for the InjectionService, covering its functionality, listener behavior, and typed injection API.
  • packages/core/src/config/injectionService.ts
    • Added a new file defining the InjectionService class, InjectionSource type, and InjectionListener type.
    • Implemented addInjection method to handle different injection sources with gating.
    • Provided addUserHint as a convenience wrapper for user_steering injections.
    • Implemented onInjection, offInjection, onUserHint, offUserHint for listener management.
    • Added methods for retrieving injections, their indices, and timestamps, and a clear method.
  • packages/core/src/config/userHintService.test.ts
    • Removed the test file for the old UserHintService.
  • packages/core/src/config/userHintService.ts
    • Removed the UserHintService file, as its functionality has been superseded by InjectionService.
  • packages/core/src/index.ts
    • Exported ExecutionLifecycleService and InjectionService.
  • packages/core/src/services/executionLifecycleService.test.ts
    • Added a new test suite for 'Background Completion Listeners' within ExecutionLifecycleService.
    • Tested onBackgroundComplete listener behavior with custom formatInjection callbacks, error handling, and null injectionText scenarios.
    • Verified that listeners are not fired for non-backgrounded or killed executions.
    • Confirmed that offBackgroundComplete correctly removes listeners.
  • packages/core/src/services/executionLifecycleService.ts
    • Defined FormatInjectionFn type for custom output formatting.
    • Added backgrounded flag and formatInjection property to ManagedExecutionBase interface.
    • Defined BackgroundCompletionInfo and BackgroundCompletionListener types for background completion events.
    • Added static backgroundCompletionListeners set and methods onBackgroundComplete and offBackgroundComplete.
    • Cleared backgroundCompletionListeners during service reset.
    • Added formatInjection parameter to createExecution method.
    • Updated completeExecution to fire background completion listeners if the execution was backgrounded and not aborted, applying formatInjection if provided.
    • Set execution.backgrounded = true in the background method.
Activity
  • The pull request was created by adamfweidman.
  • The pull request includes a detailed description outlining the changes and their purpose.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-cli gemini-cli bot reopened this Mar 15, 2026
@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli bot commented Mar 15, 2026

Thank you for linking an issue! This pull request has been automatically reopened.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a well-executed refactoring and feature addition, renaming UserHintService to the more generic InjectionService and introducing support for injecting output from backgrounded executions. However, a significant prompt injection risk has been identified: untrusted output from tools or shell commands is automatically re-injected into the model conversation without sufficient sanitization or delimitation, potentially allowing an attacker to manipulate the LLM's behavior. Furthermore, the InjectionService lacks size limits on injected content, which could lead to excessive memory usage. There is also a high-severity issue where an unhandled exception in a callback could lead to a hung state, and a suggestion has been provided to make this part of the code more robust against errors from external code like extensions, aligning with the rule to log detailed errors when catching exceptions.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 15, 2026

Size Change: +3.84 kB (+0.01%)

Total Size: 26.1 MB

Filename Size Change
./bundle/chunk-3IDW3S7Q.js 0 B -13.4 MB (removed) 🏆
./bundle/chunk-OQMXODST.js 0 B -3.62 MB (removed) 🏆
./bundle/core-A3CNBWF7.js 0 B -40.3 kB (removed) 🏆
./bundle/devtoolsService-BH3GGWPB.js 0 B -27.7 kB (removed) 🏆
./bundle/interactiveCli-UOT5KKHY.js 0 B -1.59 MB (removed) 🏆
./bundle/oauth2-provider-7UJH6NT6.js 0 B -9.19 kB (removed) 🏆
./bundle/chunk-CPASWHSC.js 3.62 MB +3.62 MB (new file) 🆕
./bundle/chunk-NTI5V5HN.js 13.4 MB +13.4 MB (new file) 🆕
./bundle/core-CFFB5SAW.js 40.5 kB +40.5 kB (new file) 🆕
./bundle/devtoolsService-HETU2SMF.js 27.7 kB +27.7 kB (new file) 🆕
./bundle/interactiveCli-ABYTBY7S.js 1.59 MB +1.59 MB (new file) 🆕
./bundle/oauth2-provider-X27FPEWS.js 9.19 kB +9.19 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size
./bundle/chunk-34MYV7JD.js 2.45 kB
./bundle/chunk-37ZTTFQF.js 966 kB
./bundle/chunk-5AUYMPVF.js 858 B
./bundle/chunk-664ZODQF.js 124 kB
./bundle/chunk-DAHVX5MI.js 206 kB
./bundle/chunk-IUUIT4SU.js 56.5 kB
./bundle/chunk-RJTRUG2J.js 39.8 kB
./bundle/chunk-VN2KBIC6.js 1.95 MB
./bundle/devtools-36NN55EP.js 696 kB
./bundle/dist-T73EYRDX.js 356 B
./bundle/gemini.js 695 kB
./bundle/getMachineId-bsd-TXG52NKR.js 1.55 kB
./bundle/getMachineId-darwin-7OE4DDZ6.js 1.55 kB
./bundle/getMachineId-linux-SHIFKOOX.js 1.34 kB
./bundle/getMachineId-unsupported-5U5DOEYY.js 1.06 kB
./bundle/getMachineId-win-6KLLGOI4.js 1.72 kB
./bundle/memoryDiscovery-A4UZQ6IE.js 922 B
./bundle/multipart-parser-KPBZEGQU.js 11.7 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 221 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 227 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 11.5 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B
./bundle/sandbox-macos-permissive-open.sb 890 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB
./bundle/sandbox-macos-strict-open.sb 4.82 kB
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB
./bundle/src-QVCVGIUX.js 47 kB
./bundle/tree-sitter-7U6MW5PS.js 274 kB
./bundle/tree-sitter-bash-34ZGLXVX.js 1.84 MB
./bundle/undici-4X2YZID5.js 360 B

compressed-size-action

@gemini-cli gemini-cli bot added the status/need-issue Pull requests that need to have an associated issue. label Mar 15, 2026
Wire ExecutionLifecycleService.setInjectionService() in Config constructor
so backgrounded executions inject directly via settleExecution instead of
routing through a useEffect bridge in AppContainer.
@adamfweidman adamfweidman changed the title refactor(core): rename UserHintService to InjectionService and add background completion support refactor(core): introduce InjectionService and wire background completions natively in backend Mar 15, 2026
…nt loop

The agent loop in local-executor now listens via onInjection (all sources)
instead of onUserHint (steering only), picking up background completions
between turns. This removes the separate bg completion useEffect, refs,
state, and callback from AppContainer entirely.
…rface

Remove legacy onUserHint/offUserHint/addUserHint methods. All callers
now use addInjection(text, source) and onInjection/offInjection with
source-based filtering where needed.
@adamfweidman adamfweidman changed the title refactor(core): introduce InjectionService and wire background completions natively in backend refactor(core): introduce InjectionService with source-aware injection and backend-native background completions Mar 16, 2026
…ve dead code

Rename getUserHints/getUserHintsAfter/getLatestHintIndex to
getInjections/getInjectionsAfter/getLatestInjectionIndex with optional
source filter so bg completions don't get formatted as user hints.

Swap unshift ordering so bg completions appear before user hints in the
message — the model sees context before the user's reaction to it.

Remove unused getLastUserHintAt().
@adamfweidman adamfweidman force-pushed the afw/injection-service-infra branch from 7d53152 to 2727a87 Compare March 16, 2026 02:16
@adamfweidman
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the UserHintService into a more generic InjectionService, enabling different sources of content injection like user steering and background task completions. This is a key step for supporting background tasks, with InjectionService handling injections and ExecutionLifecycleService wiring up the completion of background tasks to this new service. However, a critical security concern has been identified: a potential prompt injection vulnerability in the LocalAgentExecutor due to untrusted output from backgrounded executions being directly concatenated into the model's prompt without proper delimiting or sanitization, which could allow a malicious background process to manipulate the main agent's behavior. This aligns with rules emphasizing sanitization and avoiding direct inclusion of untrusted input in LLM content. Additionally, high-severity robustness issues were found in the new listener notification loops within both InjectionService and ExecutionLifecycleService, where unhandled errors could lead to application crashes. Implementing try...catch blocks with detailed error logging, as per established rules, is suggested to enhance event broadcasting resilience.

Copy link
Copy Markdown
Collaborator

@mattKorwel mattKorwel left a comment

Choose a reason for hiding this comment

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

Super excited to get this in. I acutally asked gemini to start writing the background notifications tonight, but thankfully had ti check if any outstanding work was in flight and found this 🙇

Approving now as I'm sure you're working through things. But as the review bot found, the local code review also called out the below:

Key Review Findings

  1. Security Vulnerability (Prompt Injection)
  • Issue: In packages/core/src/agents/local-executor.ts, output from background tasks (bgText) is concatenated directly into the prompt without any sanitization or protective wrapping.
  • Risk: A malicious background process could output text designed to hijack the agent's behavior (e.g., instructions to delete files or exfiltrate data).
  • Recommendation: Wrap background completions in protective tags (like <background_completion>) similar to how user hints are handled.
  1. Robustness Concerns
  • Issue: The listener notification loops in InjectionService and ExecutionLifecycleService lack error handling.
  • Risk: If a single listener (e.g., in a plugin or internal component) throws an error, it will crash the entire service, potentially bringing down the CLI session.
  • Recommendation: Implement try-catch blocks within the broadcast loops to ensure a failing listener doesn't impact others.
  1. Integration Testing Gaps
  • Issue: While the new services have unit tests, the integration into local-executor.ts is not fully verified.
  • Recommendation: Update local-executor.test.ts to include test cases that specifically verify the new background_completion injection flow.

Overall Recommendation: REQUEST CHANGES
The PR is a strong step forward but requires fixes for the formatting errors, the prompt injection vulnerability, and the robustness issues before it can be safely merged.

@mattKorwel mattKorwel self-requested a review March 16, 2026 04:57
Copy link
Copy Markdown
Collaborator

@mattKorwel mattKorwel left a comment

Choose a reason for hiding this comment

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

Actually don't want it to accidentally auto merge. Ping me as soon as its cleaned up and we'll submit.

Wrap background completion output in <background_output> XML tags with
inline instructions to treat as data, consistent with <user_input> tags
used for user steering hints.

Guard listener iteration in InjectionService.addInjection and
ExecutionLifecycleService.settleExecution with try/catch so a throwing
listener doesn't block subsequent listeners or crash the caller.
@adamfweidman adamfweidman force-pushed the afw/injection-service-infra branch from 2d16fde to 1dc55b2 Compare March 16, 2026 15:01
@adamfweidman adamfweidman requested a review from mattKorwel March 16, 2026 15:55
… flow

Tests cover XML tag wrapping with safety instruction, ordering
(background completions before user hints), and source filtering
to prevent background output from leaking into user hint getters.
Copy link
Copy Markdown
Collaborator

@mattKorwel mattKorwel left a comment

Choose a reason for hiding this comment

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

🎉

@mattKorwel mattKorwel added this pull request to the merge queue Mar 16, 2026
Merged via the queue into main with commit 44ce90d Mar 16, 2026
27 checks passed
@mattKorwel mattKorwel deleted the afw/injection-service-infra branch March 16, 2026 21:20
kunal-10-cloud pushed a commit to kunal-10-cloud/gemini-cli that referenced this pull request Mar 21, 2026
ProthamD pushed a commit to ProthamD/gemini-cli that referenced this pull request Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants