PendingToolInterrupt
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:212
A single tool call a human-in-the-loop interrupt is waiting on, surfaced from the
suspended graph state so a consumer (the interactive session) can render an approve/reject
prompt. Mirrors LangChain’s HITL ActionRequest (tool name + the args it would run with).
Properties
Section titled “Properties”args:
Record<string,unknown>
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:214
denyPreview?
Section titled “denyPreview?”
optionaldenyPreview?:string
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:323
[[TUI-C26]] §6 — what the menu’s always reject choice will record, in the object form the user would write in a config file. The deny mirror of grantPreview, shown at the moment of the choice for the same reason: a control that does not say what it stores is one the user has to guess at.
Its availability is NOT the grant’s, and reading one off the other is the mistake this
field exists to prevent. The matcher’s own rule is undecidable → no match on the allow side, a
match on the deny side, so a command the gate cannot statically resolve can be refused
permanently even though it can never be allowed permanently. always reject also stays offered
on a catastrophic verdict, where §4.2 withdraws every sticky grant: sticky refusal is safe in
every direction, and it is only the grant that is ever made harder.
Absent only where the entry grammar cannot hold one at all — today, an MCP call whose server
could not be attributed (the grammar’s server cannot be empty). Absent means the control is
not offered, never offered-and-disabled.
denySummary?
Section titled “denySummary?”
optionaldenySummary?:string
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:329
§6 — the same deny entry in the words the control is written in, through the one-liner every other provenance message uses. Present exactly when denyPreview is, since both are rendered from the one entry the runner would record.
escalatedBy?
Section titled “escalatedBy?”
optionalescalatedBy?:string
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:272
EXT-71 §3.2 — when this call reached the human because a declared approvals.escalate entry
matched it, the entry that fired, rendered for display. It is the provenance the prompt shows:
an escalation the user cannot trace to the line they wrote reads as the gate malfunctioning.
Absent whenever the escalation came from the rung or the rater instead.
grantPreview?
Section titled “grantPreview?”
optionalgrantPreview?:string
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:290
EXT-71 §6 — what a sticky choice will store, rendered in the object form the user would
write in a config file, e.g. { "type": "shell", "matcher": "exact", "pattern": "npm test" }.
The menu MUST show this at the moment of the choice, on every surface: the user is shown the
thing they are agreeing to rather than a generalization of it, which is what makes the display
honest and cheap at once.
For a tool call the stored thing is the tool, not the arguments (§4.7.4), so this reads
e.g. { "type": "mcpTool", "server": "fetcher", "matcher": "exact", "pattern": "fetch_url", "host": "docs.internal.example" }. That is the one place a grant is deliberately broader than
what the human was shown, which is why the display carries the most weight there.
Absent exactly where no sticky grant is on offer — a catastrophic outcome (§4.2 withdraws
the persistent grants), a rung that remembers nothing, a command that does not statically
resolve, or a tool call naming more than one host (which has no honest single-host entry) — so
a prompt never advertises a control that has already been withdrawn.
grantSummary?
Section titled “grantSummary?”
optionalgrantSummary?:string
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:305
§6 — the same grant in the words a menu control is written in: npm test for a shell
command, tool gth_web_fetch (host docs.internal.example) or mcpTool jira/create_issue for a
tool call. It is what the always approve control names, so the control reads as
always approve this tool for this host rather than as a bare key.
It is rendered by describeApprovalEntry — the one-liner every other provenance message uses,
including the §4.7.4 notice that later withdraws the grant. Sharing the renderer is the point:
a menu that describes a grant differently from the notice that withdraws it is how a user stops
trusting either.
Present exactly when grantPreview is, since both are rendered from the one entry
recordApproval would write.
optionalid?:string
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:231
[[TUI-C69]] §5.4 — the LangChain tool_call id this interrupt is holding, so a decision
about the call can be attributed to the call on screen.
LangChain’s HITL ActionRequest is { name, args, description } and carries no id, so the
suspended-state reader recovers it from the AI message the requests were built from: the
same getState snapshot that holds tasks[].interrupts also holds values.messages, whose
last AIMessage carries the very tool_calls the middleware filtered. Matched back by name
and arguments within that one message, in order, so it is the call’s own id rather than a
correlation.
It is a display attribution, never a decision input. Nothing about whether a call is allowed may read it: the gate decides on the command and the subject, and an id is neither. Optional because the recovery is defensive at every step — an unexpected state shape yields an interrupt with no id, and every consumer of the id treats its absence as “attribute nothing”.
name:
string
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:213
negotiationAttempts?
Section titled “negotiationAttempts?”
optionalnegotiationAttempts?:number
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:361
[[EXT-29]] §5.3 — how many attempts the agent actually made, counted since a human was last involved rather than since the last approved call.
Persistence is the single most decision-relevant fact this block carries, and it is measured
against a real under-report: on the escalation this comes from, the agent proposed the same
command five times, was refused every time, and the human was shown three, because the two
approved calls it made in between (git stash, git status) each erased the rounds before
them. [[EXT-108]] removed that erasure, so the array and this count are now the same set as
they leave the state machine.
It stays a separate number for the reason that survives: both values are handed over whole and the renderer is what slices for the screen, so sending both is what lets it number the rounds it prints by their true attempt number instead of from one.
Present exactly when negotiationRounds is, and never smaller than its length.
negotiationRounds?
Section titled “negotiationRounds?”
optionalnegotiationRounds?: readonlyRaterNegotiationRound[]
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:343
[[EXT-29]] §6 — every round of the §5 negotiation that preceded this escalation, oldest first, when one did.
The user is not asked to rule on the final command in isolation: that the agent proposed
git reset --hard origin/main three times unchanged, against two rejections that each told it
what to fix, is itself the most important thing on the screen, and it is invisible if only the
last attempt is shown. core/shell/negotiation.ts’s renderNegotiationTranscript is the
shared renderer, so two surfaces cannot describe one exchange two ways.
Absent for every escalation that had no negotiation — catastrophic (§4.2 gives it no rounds
at all), a declared approvals.escalate entry, an unrated rung, a tool subject.
safetyVerdict?
Section titled “safetyVerdict?”
optionalsafetyVerdict?:object
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:265
CFG-26 — when the AI rater escalated this run_shell_command to the human (rather than
approving it or bouncing it back to the model), the rater’s verdict is attached here so the
approval surface can show an “AI rater (
outcome
Section titled “outcome”outcome:
"safe"|"destructive"|"catastrophic"|"attack"
reason
Section titled “reason”reason:
string
suggestedTool?
Section titled “suggestedTool?”
optionalsuggestedTool?:string
subject?
Section titled “subject?”
optionalsubject?:ApprovalSubject
Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/core/types.ts:258
[[TUI-C67]] — what kind of call this is, as the gate itself decided it (§3.1/§4.7.5): a
shell subject carrying the command, a tool subject, or an mcpTool subject carrying the
user’s own mcpServers key. It is the discriminator decideToolApprovalInner matched rules
on, travelling to the surface so the prompt can branch on the same one.
Both terminal approval surfaces used to open with the agent wants to run a shell command,
which EXT-80 made false for the most common prompt in manual and write — a file write, an
MCP call or a custom tool, each announced as a shell command.
core/approvals/promptHeader.ts renders the sentence from this field for both of them;
re-deriving the kind from name on each surface would be a second classifier, free to
disagree with the one that gated the call.
The ACP server is currently that second classifier, and the rule above still stands.
acpPermissions.ts titles its permission request from its own shellCommandOf, keyed on
name rather than on this field, which it never reads — a divergence pinned by an
assertion in packages/agent/spec/acpServer.spec.ts. Closing it means reading this field there
rather than the tool name, and that is [[TUI-C89]]’s work; do not read the divergence’s
existence as licence to re-derive the kind anywhere else.
The runner attaches it to every interrupt it hands the approval callback, including the
plainest case where nothing else on this interface is set. Optional only because an interrupt
read back out of graph state (getPendingToolInterrupts) is assembled before any subject
exists; a surface that renders through promptHeader and is handed one of those renders the
generic tool sentence, never a wrong one.