fix: send hook_config to server in RemoteConversation#2115
Conversation
The hook_config was being accepted but never sent to the server when creating a conversation via RemoteConversation. This meant that hooks for PreToolUse, PostToolUse, UserPromptSubmit, and Stop were never executed server-side. Changes: - Add hook_config to the payload when creating conversations on server - Update example to demonstrate hook configuration with RemoteConversation - Remove outdated warning since hooks are now sent to server Co-authored-by: openhands <openhands@all-hands.dev>
all-hands-bot
left a comment
There was a problem hiding this comment.
Taste Rating: 🟡 Acceptable - Core fix is correct, but example comments need updating.
VERDICT: ✅ Worth merging after fixing the outdated comments. The bug fix itself is solid.
examples/02_remote_agent_server/01_convo_with_local_agent_server.py
Outdated
Show resolved
Hide resolved
examples/02_remote_agent_server/01_convo_with_local_agent_server.py
Outdated
Show resolved
Hide resolved
Verifies that hook_config is properly sent to the server when creating a RemoteConversation, ensuring server-side hooks (PreToolUse, PostToolUse, etc.) can execute. Co-authored-by: openhands <openhands@all-hands.dev>
Coverage Report •
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Add HookExecutionEvent to provide full observability into hook execution: - Create new event type HookExecutionEvent with fields for hook type, command, success/blocked status, stdout/stderr, and context - Update HookEventProcessor to emit HookExecutionEvent for all hook types (PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop) - Add emit_hook_events parameter to control event emission (default True) - Add 'hook' to SourceType for proper event typing - Export HookExecutionEvent from openhands.sdk public API - Add comprehensive tests for HookExecutionEvent emission - Fix existing tests to handle new events being emitted The HookExecutionEvent includes: - hook_event_type: Type of hook (PreToolUse, PostToolUse, etc.) - hook_command: The command that was executed - success/blocked/exit_code: Execution result - stdout/stderr: Command output - reason/additional_context/error: Hook response data - action_id/message_id: Related event IDs - hook_input: Input data passed to hook Co-authored-by: openhands <openhands@all-hands.dev>
…er.py Co-authored-by: OpenHands Bot <contact@all-hands.dev>
examples/02_remote_agent_server/01_convo_with_local_agent_server.py
Outdated
Show resolved
Hide resolved
- Add hook_config field to ConversationState for persistence - Add hook_config field to SystemPromptEvent for observability in event stream - Update visualize and __str__ methods to display hook info - Store final hook_config in state when plugins are loaded (LocalConversation) - Add hook_config property to RemoteState to fetch from server - Add hook_config to ConversationStateProtocol - Resolve HookConfig forward reference in SystemPromptEvent via model_rebuild This enables users to see what hooks are configured for a conversation via the state API and in the SystemPromptEvent at the start of conversation. Co-authored-by: openhands <openhands@all-hands.dev>
- Add on_stop hook in addition to post_tool_use hook - Use patched LLM that returns finish tool call to trigger hooks - Assert HookExecutionEvent events are received for both PostToolUse and Stop - Verify hook execution details (success, blocked, exit_code, command) Co-authored-by: openhands <openhands@all-hands.dev>
Use git commit 2444bd1e66191598f07e7f91adcd6f1c69b78dac from OpenHands/software-agent-sdk#2115 This includes the fix for sending hook_config to server in RemoteConversation. Co-authored-by: openhands <openhands@all-hands.dev>
API breakage checks (Griffe)Result: Passed |
Agent server REST API breakage checks (OpenAPI)Result: Failed Log excerpt (first 1000 characters) |
|
@OpenHands Do a /codereview-roasted on this PR, but re-review your findings before answering to double check if the issues you first find are indeed real or as major or as minor as they seemed to be. |
|
I'm on it! enyst can track my progress at all-hands.dev |
|
🔴 Taste Rating: Needs improvement This PR fixes the headline bug (sending [CRITICAL ISSUES] (must fix)1) RemoteConversation will execute SessionStart/SessionEnd hooks twice (client + server)
Why this is bad:
Concrete fix direction:
Right now it’s “we do both” with a comment claiming it’s split. That’s not a design; it’s a bug. 2) Import-cycle hack in
|
Re-checking my review, section by sectionTaste rating (🔴 Needs improvement)Mostly accurate, but the “needs improvement” hinges almost entirely on one semantic problem (SessionStart/End). If that’s fixed/clarified, the rest is more “acceptable with a couple sharp edges” than “needs rework”. [CRITICAL ISSUES](1) “RemoteConversation will execute SessionStart/SessionEnd hooks twice”Accurate, and it is major if the intended contract is “SessionStart/SessionEnd are client-side”. Why I’m confident:
So unless the server deliberately ignores session hooks (it doesn’t), SessionStart/End are run in both places. Severity check:
So yes: this is properly labeled CRITICAL. (2) “Import-cycle hack in
|
|
@OpenHands review all the review comments above, and can you help me review all the existing diff and address all of them? You should run examples/02_remote_agent_server/01_convo_with_local_agent_server.py using the LLM_API_KEY in your env var, and make sure everything works. You can save all your output artifacts/output logs to .pr/ folder to help with human review (don't worry - it will be deleted after the PR is merged). I'm expecting:
You should keep debugging/fixing until the agent is behaving as expected. |
|
I'm on it! xingyaoww can track my progress at all-hands.dev |
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Includes output log from running examples/02_remote_agent_server/01_convo_with_local_agent_server.py after merging main into this branch. Co-authored-by: openhands <openhands@all-hands.dev>
|
✅ PR Artifacts Cleaned Up The |
Co-authored-by: openhands <openhands@all-hands.dev>
The hook runs Python syntax checks using py_compile, not pre-commit hooks. Co-authored-by: openhands <openhands@all-hands.dev>
Keep only the latest JSON mode example run log and summary. Co-authored-by: openhands <openhands@all-hands.dev>
|
I'm on it! xingyaoww can track my progress at all-hands.dev |
SummaryThe CI failure on PR #2115 ( (The second pass was ~10x slower than the first, the opposite of expected — a classic timing flake.) No code changes were needed. I simply re-ran the failed job (
Since no code changes were made, there is nothing to push. |
|
@OpenHands verify if this comment issues have been indeed fixed in the current PR diff: #2115 (comment) What about the question for the PR author? Note: When I ask you to summarize, don't summarize, just repeat verbatim what you just me. Really, I mean it, in this session just repeat what you said. |
|
I'm on it! enyst can track my progress at all-hands.dev |
|
I checked the current branch and the items in that comment are already addressed in the code:
About the remaining question for the PR author (SessionStart/SessionEnd contract):
So as implemented, SessionStart/End can run twice (client + server). If the intended contract is client-only, the payload should strip those before sending. If it’s server-only, the local execution should be removed and the docs/comments updated accordingly. |
|
@OpenHands can you help me address this issue: #2115 (comment)
Let's make it "server-only". You should just push when you finish the implementation, you can gh watch the CI for the test status instead of running it yourself (live server test will crash your runtime now :( |
|
I'm on it! xingyaoww can track my progress at all-hands.dev |
Remove client-side SessionStart/SessionEnd hook execution to prevent double-execution (once on client, once on server). All hooks including SessionStart/SessionEnd are now handled exclusively server-side via the hook_config sent in the conversation creation payload. Co-authored-by: openhands <openhands@all-hands.dev>
SummaryI addressed the review comment requesting that SessionStart/SessionEnd hooks be server-only in Changes (single commit
|
Update openhands-agent-server, openhands-sdk, and openhands-tools to git commit e346ea2812c213f6300a3608652edad366364034 from OpenHands/software-agent-sdk#2115 Previously only openhands-sdk was pinned (at 70ebee0); agent-server and tools were still at the released 1.11.5. This brings all three packages to the same commit so the agent-server image includes the hook_config fix. Co-authored-by: openhands <openhands@all-hands.dev>
hook_config has nothing to do with the system prompt. It was added purely for observability, but it's already accessible via conversation.state.hook_config (ConversationState field added in this PR). Putting it in SystemPromptEvent was a misuse of the event's purpose and added unnecessary complexity (model_rebuild for forward refs, visualization code). The proper place for hook_config observability is conversation.state, which is already exposed through both LocalConversation and RemoteConversation (via RemoteState.hook_config property). Co-authored-by: openhands <openhands@all-hands.dev>
enyst
left a comment
There was a problem hiding this comment.
Thank you so much for this! Generally LGTM
…68d1ee) Merge branch 'main' into load-hooks-from-workspace to resolve conflicts, then update openhands-agent-server, openhands-sdk, and openhands-tools to git commit e68d1eec53fd68af2cf373edef018a91a2bc2aed from OpenHands/software-agent-sdk#2115 Co-authored-by: openhands <openhands@all-hands.dev>
all-hands-bot
left a comment
There was a problem hiding this comment.
Taste Rating: 🟢 Good taste - Core fix is elegant and simple
VERDICT: ✅ Approve
KEY INSIGHT: Eliminating the client-side special case by forwarding hook_config to the server is the right architectural choice. The fix is simple, correct, and well-tested.
Minor suggestion: The example file has grown from ~50 lines demonstrating basic RemoteConversation usage to ~350 lines testing complex hook denial/retry scenarios. Consider whether the hook testing portion should be a separate example file (e.g., 02_hooks_with_stop_validation.py) to keep 01_convo_with_local_agent_server.py focused on basic usage. Not blocking - the comprehensive demonstration is valuable, just might be better organized as separate examples.
| @@ -168,42 +173,155 @@ def event_callback(event): | |||
| ) | |||
There was a problem hiding this comment.
🟡 Suggestion: This example has grown significantly in complexity. The original showed basic RemoteConversation usage (~50 lines). Now it's a comprehensive hook testing harness (~350 lines) that deliberately creates broken Python files to test stop hook denial and retry logic.
For a "01_" introductory example, consider splitting this into:
01_convo_with_local_agent_server.py- basic usage (~50 lines)02_hooks_with_stop_validation.py- comprehensive hook testing (current content)
The comprehensive demonstration is valuable for showing hook capabilities, but might be better as a separate, clearly-labeled advanced example.
Not blocking - this is an organizational suggestion to improve discoverability for users learning the basics.
Summary
Fixed a bug where
hook_configwas being accepted byRemoteConversationbut never sent to the server when creating a conversation.Problem
When using
RemoteConversationwithhook_config, hooks forPreToolUse,PostToolUse,UserPromptSubmit, andStopwere never executed server-side because thehook_configwasn't included in the conversation creation payload.The client was showing a misleading warning "RemoteConversation only supports SessionStart/SessionEnd hooks" when it should have been sending the hook configuration to the server where these hooks would be executed.
Changes
hook_configto the payload when creating conversations via the REST API02_remote_agent_server/01_convo_with_local_agent_server.pyto demonstrate hook configuration withRemoteConversationTesting
Ran the modified example to verify:
Related Documentation
Agent Server images for this PR
• GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server
Variants & Base Images
eclipse-temurin:17-jdknikolaik/python-nodejs:python3.13-nodejs22golang:1.21-bookwormPull (multi-arch manifest)
# Each variant is a multi-arch manifest supporting both amd64 and arm64 docker pull ghcr.io/openhands/agent-server:76ce8b7-pythonRun
All tags pushed for this build
About Multi-Architecture Support
76ce8b7-python) is a multi-arch manifest supporting both amd64 and arm6476ce8b7-python-amd64) are also available if needed