Skip to content

fix: reject HITL edits that repoint the generic call tool's action - #176

Merged
sjvans merged 1 commit into
per-actionfrom
fix/hitl-edit-action-guard
Oct 4, 2026
Merged

sjvans merged 1 commit into
per-actionfrom
fix/hitl-edit-action-guard

Conversation

@sjvans

@sjvans sjvans commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Problem

The generic combined call tool carries its target action in args.action, and HITL gates it per-call via the when predicate added in #170 — so an interrupt only fires when the targeted action is gated (@agent.hitl / @Common.IsActionCritical).

Nothing stopped a resume's edit decision from changing args.action to a different action. That different action would then execute under the approval the human granted for the gated one — including an action that isn't HITL-gated at all. Approve-then-edit becomes an escalation path.

Fix

Guard at the edit decision boundary (guardHitlEdits in srv/handlers/graph-executor/hitl.js): for a generic call tool, an edit may change the parameters but not args.action. If it tries to repoint the action, the resume is rejected. Per-action tools are unaffected — their name is the action and they carry no args.action to swap. Non-edit decisions pass through.

Scoped deliberately: it forbids changing the action outright (the tightest safe invariant), rather than consulting the service model to allow swaps between gated actions. Editing is for tuning parameters, not re-choosing the action.

Tests

tests/integration/hitl-edit-escalation-guard.test.js — parameters-only edit allowed, same-action edit allowed, action-swap rejected, per-action pass-through, non-edit pass-through, and positional matching across multiple decisions. Full non-hybrid suite green (563 passed).

Context

Follow-up to the #174 discussion. The two-way action/call transform in #174 was dropped in favour of @Akatuoro's test-only approach on #166 (keep the technical call name, read args.action for display). This PR keeps just the one piece that wasn't about presentation: the escalation guard.

The generic "call" tool carries its target in args.action, and HITL gates it
per-call via a `when` predicate — so the interrupt only fires when the targeted
action is gated. Nothing stopped a resume's edit decision from changing
args.action to a *different* action, which would then run under the approval
granted for the gated one — including an action that is not HITL-gated at all.

Guard the edit at the decision boundary: an edit may change the parameters but
not the action. Per-action tools are unaffected (their name is the action and
carries no args.action to swap).
@sjvans
sjvans marked this pull request as ready for review October 4, 2026 19:36
@sjvans
sjvans requested review from a team as code owners October 4, 2026 19:36
@hyperspace-pr-bot

Copy link
Copy Markdown

Summary

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


Title

fix: prevent HITL generic call edits from changing actions

Category

Bug Fix 🐛

Summary

Adds a HITL resume safeguard that prevents edit decisions for the generic call tool from changing the gated target action.

The generic call tool stores the invoked action in args.action, while HITL gating is applied per invocation. Without this guard, a user could approve an interrupted gated action and then edit the resume payload to point at a different action, including one that was not HITL-gated.

Changes

  • Added guardHitlEdits in srv/handlers/graph-executor/hitl.js.
  • Rejects generic call tool edits when editedAction.args.action differs from the originally interrupted action.
  • Allows parameter-only edits and edits that keep the same action.
  • Leaves per-action tools and non-edit decisions unchanged.
  • Invokes the guard before resuming graph execution.
  • Added integration coverage for:
    • parameter-only edits
    • same-action edits
    • action-swap rejection
    • per-action pass-through
    • non-edit pass-through
    • positional matching across multiple HITL decisions

Related

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.69

@sjvans
sjvans merged commit b87de1a into per-action Oct 4, 2026
1 check passed
@sjvans
sjvans deleted the fix/hitl-edit-action-guard branch October 4, 2026 19:37
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