Skip to content

fix(agent_loop): apply agent_before hook return value to context#623

Open
Kailigithub wants to merge 1 commit into
lsdefine:mainfrom
Kailigithub:fix/issue-537-plugin-hook-context-modification
Open

fix(agent_loop): apply agent_before hook return value to context#623
Kailigithub wants to merge 1 commit into
lsdefine:mainfrom
Kailigithub:fix/issue-537-plugin-hook-context-modification

Conversation

@Kailigithub

Copy link
Copy Markdown
Contributor

Problem

plugins/hooks.py::trigger() already supports returning a dict to mutate the
agent context, but agent_runner_loop() in agent_loop.py discarded the
return value of _hook('agent_before', ...). Plugins that wanted to modify
system_prompt, user_input, or initial_user_content had their changes
silently dropped.

This is the same class of issue called out in #537 — only the
agent_before hook is affected here; other hooks (tool_before,
tool_after, turn_*, llm_*, agent_after) are intentionally left
unchanged because they are observed but not expected to mutate the
in-flight state.

Fix

Capture the return value of _hook('agent_before', locals()):

  • When a dict is returned, apply system_prompt / user_input /
    initial_user_content overrides to the messages list.
  • When the hook returns None (the historical contract) or a
    non-dict value, the loop proceeds with the original arguments.
  • The initial_user_content field takes precedence over user_input,
    preserving the existing argument-resolution order.

Backward compatibility: existing read-only hooks (logging, metrics,
telemetry) keep working without modification — they simply ignore the
return value internally, and agent_runner_loop ignores non-dict
return values.

Verification

Eight standalone tests against agent_runner_loop using a stub client
and BaseHandler subclass:

# Scenario Result
1 Hook returns dict with system_prompt override messages[0] updated
2 Hook returns dict with user_input override messages[1] updated
3 Hook returns None (legacy contract) messages unchanged
4 Hook sets both user_input and initial_user_content initial wins
5 Hook returns a non-dict (e.g. string) messages unchanged
6 Hook mutates ctx in place without explicit return in-place change applied
7 Issue #537 reproduction: system_prompt augmentation messages[0] now contains augmented prompt
8 ruff check --select F,E9 on modified file only pre-existing findings; no new ones introduced

py_compile agent_loop.py passes; pytest tests/ (currently empty
in this repo) runs with no regressions.

Regression-test sanity: reverting this change and re-running Test 7
asserts messages[0] equals the original 'Old prompt' rather than the
augmented version — confirming the test exercises the actual bug.

Closes #537

@Kailigithub Kailigithub force-pushed the fix/issue-537-plugin-hook-context-modification branch from 073776a to bd8d3f5 Compare June 17, 2026 19:04
The plugin hook system in plugins/hooks.py already supports returning a
dict to mutate the agent context, but agent_runner_loop() discarded the
return value of _hook('agent_before', ...). This prevented plugins from
modifying system_prompt / user_input / initial_user_content.

Capture the return value; when a dict is returned, apply its overrides
to the messages list. Non-dict returns and missing keys are ignored, so
existing hooks that only inspect ctx (e.g. logging/metrics) continue to
work without modification.

Closes lsdefine#537
@Kailigithub Kailigithub force-pushed the fix/issue-537-plugin-hook-context-modification branch from bd8d3f5 to 678bbd8 Compare June 19, 2026 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Plugin hooks cannot modify context due to ignored return value

2 participants