Inject agent session info into context#1471
Conversation
It can be useful for agents to know their own session ID to provide to tools that might want to call back to the agent, generate a link to the agent session, or just have a stable identifier to use for something. Signed-off-by: Dobes Vandermeer <dobes.vandermeer@newsela.com>
There was a problem hiding this comment.
Pull request overview
Adds an internal ADK request-hook tool that injects the current session identifiers into the LLM system instructions, and wires it into the default agent/tooling setup so agents can reference their own session context.
Changes:
- Introduces
SessionInfoTool(+add_session_tool) to append session metadata intoLlmRequestsystem instructions. - Registers the session info injector in CLI agent factories and in
SkillsToolset. - Adds unit tests and updates tools documentation/export surface.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| python/packages/kagent-adk/src/kagent/adk/tools/session_tool.py | New tool that injects session info into system instructions; helper to attach it to agents. |
| python/packages/kagent-adk/src/kagent/adk/cli.py | Ensures the session info injector is added for CLI-created agents. |
| python/packages/kagent-adk/src/kagent/adk/tools/skills_toolset.py | Includes SessionInfoTool in the default skills toolset tools list. |
| python/packages/kagent-adk/src/kagent/adk/tools/init.py | Exposes SessionInfoTool and add_session_tool in the tools package exports. |
| python/packages/kagent-adk/src/kagent/adk/tools/README.md | Documents the new “SessionInfo” behavior as automatic injection. |
| python/packages/kagent-adk/tests/unittests/test_session_tool.py | Adds unit coverage validating instruction injection and non-exposure as an LLM-callable tool. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| session = tool_context.session | ||
| if not session: | ||
| return | ||
| info = ( | ||
| "kagent session:\n" | ||
| f"- session_id: {session.id or 'N/A'}\n" | ||
| f"- user_id: {session.user_id or 'N/A'}\n" | ||
| f"- app_name: {session.app_name or 'N/A'}" | ||
| ) | ||
| llm_request.append_instructions([info]) |
There was a problem hiding this comment.
SessionInfoTool injects instructions on every process_llm_request call. ADK can call process_llm_request multiple times in the same user turn (e.g., sequential tool round-trips), so this will duplicate the session info in system instructions and waste tokens. Consider guarding with a tool_context.state key (similar to PrefetchMemoryTool) or checking whether the info has already been appended before adding again.
| info = ( | ||
| "kagent session:\n" | ||
| f"- session_id: {session.id or 'N/A'}\n" | ||
| f"- user_id: {session.user_id or 'N/A'}\n" |
There was a problem hiding this comment.
This injects session.user_id into the LLM system instruction. If user_id is a real user identifier, this is a potentially sensitive value to send to external model providers and to propagate through logs/traces. Consider omitting user_id by default, redacting/hashing it, or making inclusion explicitly configurable.
| info = ( | |
| "kagent session:\n" | |
| f"- session_id: {session.id or 'N/A'}\n" | |
| f"- user_id: {session.user_id or 'N/A'}\n" | |
| user_id_display = "[redacted]" if getattr(session, "user_id", None) else "N/A" | |
| info = ( | |
| "kagent session:\n" | |
| f"- session_id: {session.id or 'N/A'}\n" | |
| f"- user_id: {user_id_display}\n" |
| root_agent = agent_config.to_agent(app_cfg.name, sts_integration) | ||
|
|
||
| add_session_tool(root_agent) | ||
| maybe_add_skills(root_agent) |
There was a problem hiding this comment.
add_session_tool() is now invoked unconditionally for all CLI entrypoints. Since this changes the prompt for every request (and may expose identifiers), consider gating it behind configuration (e.g., AgentConfig/CRD field or an env var like KAGENT_INJECT_SESSION_INFO=true) so operators can disable it in sensitive or token-constrained deployments.
| self.bash_tool = BashTool(skills_directory) | ||
| self.session_info_tool = SessionInfoTool() | ||
|
|
||
| @override | ||
| async def get_tools(self, readonly_context: Optional[ReadonlyContext] = None) -> List[BaseTool]: | ||
| """Get all skills tools. | ||
|
|
||
| Returns: | ||
| List containing all skills tools: skills, read, write, edit, and bash. | ||
| List containing all skills tools: skills, read, write, edit, bash, and session info. | ||
| """ | ||
| return [ | ||
| self.skills_tool, | ||
| self.read_file_tool, | ||
| self.write_file_tool, | ||
| self.edit_file_tool, | ||
| self.bash_tool, | ||
| self.session_info_tool, |
There was a problem hiding this comment.
SkillsToolset now always includes SessionInfoTool, while the CLI also appends SessionInfoTool via add_session_tool(). If a consumer uses SkillsToolset and also calls add_session_tool(), the session info may be injected multiple times. A shared tool_context.state guard inside SessionInfoTool (or avoiding double registration) would prevent duplicate injection regardless of how tools are composed.
| | **ReadFile** | Read files with line numbers | `read_file("skills/data-analysis/config.json")` | | ||
| | **WriteFile** | Create/overwrite files | `write_file("outputs/report.pdf", data)` | | ||
| | **EditFile** | Precise string replacements | `edit_file("script.py", old="x", new="y")` | | ||
| | **SessionInfo**| Auto-injects session details into system instructions | *(automatic — no tool call needed)* | |
There was a problem hiding this comment.
The README calls this tool “SessionInfo” and says it’s automatic/no tool call needed, but the actual injected tool name is get_session_info and it’s registered on agents. Consider clarifying in the docs that it’s an internal request hook (not an LLM-callable tool) and documenting exactly which fields are injected (and whether user_id is included).
EItanya
left a comment
There was a problem hiding this comment.
This review was generated by Claude.
Thanks for working on this — having session context available to agents is a useful feature!
A couple of architectural suggestions:
Consider whether this needs to be a Tool
SessionInfoTool doesn't implement _get_declaration() or run_async() — the LLM never actually calls it. It solely uses the process_llm_request hook to inject instructions before each LLM call. This makes it fundamentally different from the other tools in the tools list (BashTool, ReadFileTool, etc.), which are all LLM-callable.
It might be a better fit to handle this as part of the agent's instruction construction — similar to how memory instructions are appended in _configure_memory() in types.py. That approach would make the intent clearer (this is static context injection, not a tool the LLM interacts with) and avoid mixing two different concepts in the tools list.
Consider gating behind configuration
Right now session info injection is unconditional — every agent gets it regardless of whether it needs it. The existing pattern for optional capabilities like memory tools is to gate them behind agent configuration (see AgentConfig._configure_memory()). Adding a similar config flag (or even an environment variable) would let deployers opt in rather than having session metadata injected into every LLM call by default. This is especially worth considering since user_id is included in the injected content, which may have privacy implications depending on the deployment context.
It can be useful for agents to know their own session ID to provide to tools that might want to call back to the agent, generate a link to the agent session, or just have a stable identifier to use for something.
One open question I have is whether this should be always-on, or if it should be configurable somewhere (agent CRD? env var?)