Let the HITL `when` predicate read tool metadata, and add an `interruptOn` catch-all
Summary
humanInTheLoopMiddleware (in the langchain package) gained a when predicate on InterruptOnConfig in #11012, but two gaps prevent using it for property-based gating — e.g. "pause before any tool whose metadata marks it destructive":
A. when receives tool: undefined. In the afterModel path the ToolCallRequest is built with tool set to undefined, so a predicate can only read toolCall (name + args), state, and runtime — never the tool's description/schema/metadata.annotations.
B. interruptOn is keyed strictly by tool name, with no catch-all. There is no wildcard/default entry, so you cannot express "apply this predicate to every tool" — which is required when tool names are not known ahead of time (e.g. tools discovered dynamically/lazily).
Together, even with when present you can neither target unknown-named tools nor read their metadata.
Proposal
Two small, backward-compatible changes:
- Populate
request.toolin thewhenpath. Expose the agent's registered tools to middleware hooks (via a new optionalruntime.tools) and resolve the tool by name sowhencan read its metadata. Dynamically-registered tools still resolve toundefined(unchanged semantics). - Add a
"*"catch-all key tointerruptOn, applied to any tool without an explicit entry. An exact tool-name entry always wins; an explicitfalseopts a tool out even when"*"is present; absent + no wildcard stays auto-approve, so existing configs are unchanged.
Example:
humanInTheLoopMiddleware({
interruptOn: {
// Pause before any tool whose metadata marks it destructive,
// even when tool names are not enumerated upfront.
"*": {
allowedDecisions: ["approve", "reject"],
when: (request) =>
(request.tool as {
metadata?: { annotations?: { destructiveHint?: boolean } };
})?.metadata?.annotations?.destructiveHint === true,
},
// An exact-name entry overrides the "*" catch-all.
read_file: false,
},
});Parity note
The Python langchain package currently does neither (its when also receives tool=None, and it has no catch-all), so this is a JS-first enhancement rather than parity. Happy to open a matching Python PR if maintainers would like the two aligned.
Willing to contribute
Yes — I have a branch with the implementation, unit tests, and a type test ready to open as a PR referencing this issue.
Source: langchain-ai/langchainjs