Skip to content

Conversation

@hemasekhar-p
Copy link

This PR introduces the Stale Issue Auditor, a sample agent that leverages the Google ADK to automate repository health. Unlike standard time-based bots, it uses an LLM to contextualize inactivity, distinguishing between true abandonment and active maintainer investigation. The agent manages the full issue lifecycle—labeling, warning, and closing—based on configurable thresholds, while including safeguards to detect silent description edits by authors.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @hemasekhar-p, 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 sophisticated Stale Issue Auditor agent designed to enhance repository health by intelligently managing inactive GitHub issues. This agent, built using the Google ADK, distinguishes between genuinely abandoned issues and those under active maintainer investigation by leveraging a Large Language Model (LLM). It automates the entire lifecycle of an issue, from initial labeling and warning to eventual closure, while also incorporating mechanisms to detect and flag silent edits to issue descriptions by authors. Additionally, several core ADK components were refined to support this new agent's functionality and improve overall event and session management.

Highlights

  • New Stale Issue Auditor Agent: Introduced a new Stale Issue Auditor agent that leverages the Google ADK and an LLM to intelligently manage GitHub issues, moving beyond simple time-based automation.
  • LLM-Driven Issue Management: The agent uses an LLM to contextualize issue inactivity, allowing it to differentiate between truly abandoned issues and those under active maintainer investigation.
  • Automated Issue Lifecycle: Implemented automated labeling, warning, and closing of issues based on configurable thresholds, streamlining repository maintenance.
  • Silent Edit Safeguards: Incorporated safeguards to detect and alert maintainers about silent description edits by issue authors, ensuring transparency and preventing abuse.
  • Core ADK Enhancements: Updated core ADK components including InvocationContext to support callback context data, EventActions for deleted artifact tracking, and GeminiLlmConnection for improved LLM response handling.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • contrib/samples/pom.xml
    • Added the new stale-agent module to the samples aggregator.
    • Removed the a2a_remote module from the samples aggregator.
    • Adjusted XML formatting for consistency.
  • contrib/samples/stale-agent/pom.xml
    • Added a new Maven POM file for the stale-agent project.
    • Configured dependencies for Google ADK, GitHub API, Apache HttpClient, SLF4J, and Jackson.
    • Defined properties for Java and Maven compiler versions.
    • Included spring-boot-maven-plugin and exec-maven-plugin to enable running the StaleBotApp.
  • contrib/samples/stale-agent/src/main/java/com/google/adk/samples/stale/StaleBotApp.java
    • Added the main application class for the Stale Bot.
    • Implemented logic to fetch old open issues from GitHub.
    • Introduced asynchronous processing of issues with a configurable concurrency limit.
    • Utilized InMemoryRunner to execute the StaleAgent for each issue.
    • Logged processing progress, API calls, and average processing time for issues.
  • contrib/samples/stale-agent/src/main/java/com/google/adk/samples/stale/agent/StaleAgent.java
    • Added the core StaleAgent class, extending LlmAgent.
    • Implemented methods for loading and formatting the LLM prompt with dynamic repository details.
    • Included logic for caching GitHub maintainers to optimize API calls.
    • Defined fetchGraphqlData to retrieve comprehensive issue state using the GitHub GraphQL API.
    • Implemented buildHistoryTimeline and replayHistoryToFindState to analyze issue activity and determine its current status.
    • Provided tool functions (addLabelToIssue, removeLabelFromIssue, addStaleLabelAndComment, alertMaintainerOfEdit, closeAsStale) for LLM interaction to manage issues.
  • contrib/samples/stale-agent/src/main/java/com/google/adk/samples/stale/config/StaleBotSettings.java
    • Added a configuration class for the Stale Bot.
    • Defined constants for GitHub API URL, token, owner, repo, LLM model name, and label names.
    • Configured thresholds in hours for stale and close times.
    • Included settings for concurrency, GraphQL query limits, and rate limiting between processing chunks.
  • contrib/samples/stale-agent/src/main/java/com/google/adk/samples/stale/utils/GitHubUtils.java
    • Added a utility class for GitHub API interactions.
    • Implemented methods for making GET, POST, PATCH, and DELETE requests to the GitHub API.
    • Included functionality to track API call counts for monitoring and debugging.
    • Provided a method getOldOpenIssueNumbers to search for old open issues based on a specified age.
    • Configured an Apache HttpClient with a retry strategy and timeouts for robust API communication.
  • contrib/samples/stale-agent/src/main/resources/PROMPT_INSTRUCTION.txt
    • Added the LLM prompt instruction template for the StaleAgent.
    • Defined the agent's role as a repository auditor, primary directives, and reporting format.
    • Specified stale and close thresholds for issue management.
    • Outlined a detailed decision tree for the agent to follow based on issue state, roles, and activity to determine appropriate actions.
  • core/src/main/java/com/google/adk/agents/InvocationContext.java
    • Added a callbackContextData field to InvocationContext for sharing temporary data between different parts of an invocation.
    • Introduced a populateAgentStates method to initialize agent states and end-of-agent flags from a list of events.
    • Updated the constructor, builder, equals, and hashCode methods to properly include the new callbackContextData field.
  • core/src/main/java/com/google/adk/events/Event.java
    • Modified the finalResponse method to simplify its logic, removing the check for longRunningToolIds when determining if an event is a final response.
  • core/src/main/java/com/google/adk/events/EventActions.java
    • Added a deletedArtifactIds field to EventActions to track artifacts that have been deleted.
    • Updated constructors, getters, setters, equals, hashCode, and merge methods to properly handle the new deletedArtifactIds field.
    • Extended JsonBaseModel for consistent JSON serialization.
  • core/src/main/java/com/google/adk/flows/llmflows/Contents.java
    • Modified rearrangeEventsForAsyncFunctionResponsesInHistory to simplify the condition for skipping response events, now checking if !event.functionResponses().isEmpty().
  • core/src/main/java/com/google/adk/models/GeminiLlmConnection.java
    • Refactored convertToServerResponse to return an Observable<LlmResponse>, allowing for multiple emissions for different response types (content, tool call, usage metadata).
    • Added CompositeDisposable for managing RxJava subscriptions and ensuring proper resource disposal.
    • Introduced helper methods createServerContentResponse, createToolCallResponse, createUsageMetadataResponse, and createUnknownMessageResponse for clearer response mapping.
  • core/src/main/java/com/google/adk/models/GeminiUtil.java
    • Added a toGenerateContentResponseUsageMetadata method to convert UsageMetadata objects to GenerateContentResponseUsageMetadata.
  • core/src/main/java/com/google/adk/sessions/SessionJsonConverter.java
    • Refactored convertEventToJson to use Optional.ifPresent for cleaner code and added an overload to control timestamp format (ISO-8601 string or object).
    • Updated fromApiEvent to correctly parse transferToAgent and endOfAgent fields, and to handle usageMetadata and groundingMetadata from event metadata.
    • Removed the asConcurrentMapOfAgentState method.
    • Added helper methods putIfNotEmpty for maps and collections to simplify JSON serialization logic.
  • core/src/main/java/com/google/adk/tools/AgentTool.java
    • Modified the runAsync method to directly mutate toolContext.actions() by setting skipSummarization to true, instead of creating a new EventActions object.
  • core/src/test/java/com/google/adk/agents/InvocationContextTest.java
    • Updated testToBuilder to testCopyOf and added assertions for callbackContextData.
    • Added testBuildWithCallbackContextData to verify the functionality of the new field.
    • Added tests for incrementLlmCallsCount, testSessionGetters, testAgentStatesAndEndOfAgents, testSetEndInvocation, testBranch, testDeprecatedCreateMethods, testActiveStreamingTools, testEventsCompactionConfig, testBuilderOptionalParameters, testDeprecatedConstructor, testDeprecatedConstructor_11params, and populateAgentStates_populatesAgentStatesAndEndOfAgents.
  • core/src/test/java/com/google/adk/events/EventActionsTest.java
    • Updated toBuilder_createsBuilderWithSameValues and merge_mergesAllFields to include deletedArtifactIds.
    • Added a jsonSerialization_works test for EventActions to ensure proper serialization and deserialization.
  • core/src/test/java/com/google/adk/events/EventTest.java
    • Added several new tests for the finalResponse method under various conditions, including no tool calls, tool calls, with long-running tool IDs, and with skip summarization.
  • core/src/test/java/com/google/adk/flows/llmflows/BaseLlmFlowTest.java
    • Added tests run_sharingcallbackContextDataBetweenCallbacks and run_sharingcallbackContextDataAcrossContextCopies to verify the functionality of callbackContextData across callbacks and context copies.
  • core/src/test/java/com/google/adk/models/GeminiLlmConnectionTest.java
    • Updated convertToServerResponse tests to use TestObserver and properly handle Observable emissions.
    • Modified tests for usageMetadata, toolCallCancellation, and setupComplete to reflect the new Observable behavior (emitting LlmResponse or no values).
    • Added convertToServerResponse_withContentAndUsageMetadata_emitsMultiple to test scenarios with multiple emissions from a single server message.
  • core/src/test/java/com/google/adk/sessions/SessionJsonConverterTest.java
    • Added tests for fromApiEvent_withTransferToAgent_success, convertEventToJson_complexActions_success, fromApiEvent_complexActions_success, fromApiEvent_withInvalidContentMap_returnsNullContent, fromApiEvent_withInvalidArtifactDelta_skipsInvalidEntries, and fromApiEvent_missingTimestamp_throwsException.
  • core/src/test/java/com/google/adk/tools/AgentToolTest.java
    • Added call_withSkipSummarizationAndStateDelta_propagatesStateAndSetsSkipSummarization test to verify interaction between skip summarization and state delta propagation.
  • pom.xml
    • Removed the a2a/webservice module from the root POM file.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/stale-bot.yml
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.

Copy link
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 introduces the Stale Issue Auditor, an exemplary agent demonstrating the capabilities of the Google ADK for repository health automation. The implementation is well-structured, particularly the use of GraphQL for efficient data fetching and the clear decision logic in the prompt. The accompanying core framework changes are also solid, improving robustness and adding useful features like callbackContextData. My review focuses on improving the efficiency, robustness, and consistency of the new sample agent code.

<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<adk.version>0.5.0</adk.version> <!--${project.version}</adk.version> -->
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The ADK version is hardcoded to 0.5.0, while the parent POM is using 0.5.1-SNAPSHOT. This can lead to build failures or runtime issues if the sample relies on features or fixes introduced in the newer version, which is likely given the concurrent changes to the core framework in this PR. It's best practice to inherit the version from the parent POM to ensure consistency.

Suggested change
<adk.version>0.5.0</adk.version> <!--${project.version}</adk.version> -->
<adk.version>${project.version}</adk.version>

Comment on lines +46 to +48
private static String getEnv(String key) {
return System.getenv(key);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The getEnv method returns null if an environment variable is not set. This can lead to a NullPointerException when the value is used later (e.g., in Integer.parseInt or directly). It's more robust to fail fast with a clear error message if a required environment variable is missing.

Suggested change
private static String getEnv(String key) {
return System.getenv(key);
}
private static String getEnv(String key) {
String value = System.getenv(key);
if (value == null || value.isEmpty()) {
throw new IllegalStateException("Required environment variable '" + key + "' is not set.");
}
return value;
}


public class StaleBotApp {

private static final Logger logger = Logger.getLogger(StaleBotApp.class.getName());
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This class uses java.util.logging.Logger, but the project dependencies include SLF4J, which is used in other classes like StaleAgent. For consistency across the project, it's recommended to use SLF4J here as well. This would involve changing the logger initialization and updating the logging calls (e.g., logger.info(...) instead of logger.log(Level.INFO, ...)).


logger.info("Processing Issue #" + issueNumber + "...");

InMemoryRunner localRunner = new InMemoryRunner(StaleAgent.create());
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

A new InMemoryRunner is created for each issue being processed within the loop. This is inefficient as it re-initializes the StaleAgent on every call. It would be more performant to create a single InMemoryRunner instance in the runBot method and reuse it for all issues. Note that there is already an unused InMemoryRunner instance created on line 68 which could be leveraged.

Comment on lines +170 to +171
} catch (Exception ignored) {
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The exception in this catch block is ignored. This can hide potential issues during event processing, making debugging difficult. It's better to at least log the exception at a warning or error level.

                      } catch (Exception e) {
                        logger.log(Level.WARNING, "Error processing event for issue #" + issueNumber, e);
                      }

result.put("close_threshold_days", StaleBotSettings.CLOSE_HOURS_AFTER_STALE_THRESHOLD / 24.0);
result.put("maintainers", maintainers);
result.put("issue_author", issueAuthor);
result.put("last_comment_text", state.get("last_comment_text"));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The key "last_comment_text" is added to the result map twice (on line 353 and here on line 359). This second addition is redundant and should be removed.

- **IF (Question == YES) BUT (Time == NO)**:
- **Report**: "Analysis for Issue #[number]: PENDING. Maintainer asked question, but threshold not met yet. No action."
- **IF (Question == NO) OR (Internal Discussion Check == TRUE):**
- **Report**: "Analysis for Issue #[number]: ACTIVE. Maintainer gave status update or internal discussion detected. No action." No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This line appears to be an artifact from a text editor or diff tool and is likely not an intended part of the prompt. It should be removed to avoid any potential for confusing the LLM.

@hemasekhar-p hemasekhar-p force-pushed the main branch 2 times, most recently from a1fc4a0 to e7b6b83 Compare February 11, 2026 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants