Skip to content

fix: gate @agent.hitl on the generic call tool via when predicate - #170

Merged
sjvans merged 3 commits into
per-actionfrom
hitl-tool-wiring-test
Oct 2, 2026
Merged

sjvans merged 3 commits into
per-actionfrom
hitl-tool-wiring-test

Conversation

@sjvans

@sjvans sjvans commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Problem

With the generic combined call action tool (the default since the per_action_tool flip in #166),
@agent.hitl was silently ignored.

HITL wiring in lib/agents/middleware/hitl.js built its interrupt map by matching tool.name against
srv.actions[tool.name]["@agent.hitl"]:

  • per-action tool name = submitOrder → matches the annotated action → gated
  • generic tool name = call → matches no action → interrupt map empty → not gated

The generic tool collapses every action behind one name, with the target action passed in args.action,
so a tool-name match can't express the per-action @agent.hitl granularity.

Fix

Gate the call tool per-invocation using LangChain's interruptOn[tool].when predicate, which reads the
requested action from request.toolCall.args.action and checks its @agent.hitl /
@Common.IsActionCritical annotation. Per-action tools keep their direct name-based gating.

Tests

tests/integration/hitl-tool-wiring.test.js:

  • HITL middleware is installed for an @agent.hitl action under the default (generic) config
  • the generic call tool's when predicate interrupts submitOrder but not getStock
  • per-action tools still gate by matching the action name directly

sjvans added 2 commits October 1, 2026 22:28
The generic combined 'call' action tool (the default since the
per_action_tool flip) is named 'call', which matches no entry in
srv.actions. buildHitlInterruptMap keys HITL on that match, so
humanInTheLoopMiddleware returns no middleware and @agent.hitl is
silently ignored. Non-hybrid test asserting HITL middleware is
installed for the @agent.hitl submitOrder action, plus a per_action_tool
contrast proving the cause.
The generic combined 'call' action tool (the default since the
per_action_tool flip) is named 'call', which matches no entry in
srv.actions, so buildHitlInterruptMap produced an empty map and
@agent.hitl was silently ignored.

Gate the 'call' tool per-invocation with LangChain's interruptOn[tool].when
predicate, which reads the requested action from args.action and checks its
@agent.hitl / @Common.IsActionCritical annotation. Per-action tools keep
their direct name-based gating. Export buildHitlInterruptMap so the
non-hybrid test can assert the predicate interrupts submitOrder but not
getStock.
@sjvans sjvans changed the title test: reproduce @agent.hitl drop under generic call-tool default fix: gate @agent.hitl on the generic call tool via when predicate Oct 1, 2026
@sjvans
sjvans requested review from Akatuoro, BobdenOs and danjoa October 1, 2026 22:20
@sjvans
sjvans marked this pull request as ready for review October 1, 2026 22:20
@sjvans
sjvans requested review from a team as code owners October 1, 2026 22:21
@hyperspace-pr-bot

Copy link
Copy Markdown

Summary

The following content is AI-generated and provides a summary of the pull request:


Title

fix: Gate HITL actions for generic call tool

Category

Bug Fix

Summary

This PR fixes HITL middleware wiring for the generic combined call action tool. Previously, HITL gating only worked when tool names directly matched action names, so actions annotated with @agent.hitl or @Common.IsActionCritical were skipped when routed through the generic call tool.

Changes

  • Exports buildHitlInterruptMap for direct test coverage.
  • Centralizes HITL decision options and annotation detection.
  • Preserves existing per-action tool behavior by gating tools whose names match annotated actions.
  • Adds support for the generic call tool using LangChain’s when predicate to evaluate the requested action at invocation time via request.toolCall.args.action.
  • Avoids installing HITL middleware when no actions require HITL.

Tests

Adds integration coverage for:

  • HITL annotation sanity checks in the bookshop test service.
  • Middleware installation under the default generic tool configuration.
  • Generic call tool gating for annotated actions only.
  • No-op behavior when no actions are HITL-enabled.
  • Existing per-action tool gating behavior.

Related: #166

Have you...

  • Added relevant entry to the change log?

  • 🔄 Regenerate and Update Summary
  • ✏️ Insert as PR Description (deletes this comment)
  • 🗑️ Delete comment
PR Bot Information

Version: 1.31.65

@Akatuoro Akatuoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

@sjvans
sjvans merged commit 52435e9 into per-action Oct 2, 2026
4 checks passed
@sjvans
sjvans deleted the hitl-tool-wiring-test branch October 2, 2026 06:52
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.

2 participants