Skip to content

feat(hitl): expose whether a confirmation is still pending - #1590

Merged
murdore merged 1 commit into
releasefrom
feat/hitl-pending-confirmation-accessor
Aug 29, 2026
Merged

feat(hitl): expose whether a confirmation is still pending#1590
murdore merged 1 commit into
releasefrom
feat/hitl-pending-confirmation-accessor

Conversation

@murdore

@murdore murdore commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

The gap

A HITL confirmation's pending entry holds the resolve/reject of the suspended tool call, so it lives only in the memory of the manager that issued it. Nothing outside that manager could ask whether it is still there.

That matters because emitting a response is not proof the decision landed:

  • setupHITLEventForwarding installs a permanent listener for hitl:confirmation-response at construction, so emitter.emit(...) reports a listener was invoked whether or not anything is waiting.
  • A consumer that rebuilds a session from persisted state constructs a new NeuroLink instance whose pending set is empty.
  • The emit is accepted, processUserResponse logs No pending confirmation found for ID and returns, and the consumer has no way to know.

The case that motivated this: a merchant was told a gated write had been approved when it never ran.

What this adds

HITLManager.hasPendingConfirmation(confirmationId) beside the existing getPendingCount()
NeuroLink.hasPendingHITLConfirmation(confirmationId) hitlManager is private, so the manager's accessors were unreachable from outside the facade
the predicate on the HITLManager contract type in types/hitl.ts without it the method is invisible through toolRegistry.getHITLManager() and externalServerManager.getHITLManager(), which are typed against that type rather than the class

That third one was found by review, not by me — it would have shipped as a method that two of the three access paths could not see.

Deliberate limits, documented on the method

Stated in the JSDoc rather than left to be discovered:

  • It returns false for two different situations — HITL never configured, and id unknown/already settled. Both mean "emitting here achieves nothing", which is the question being asked. A caller needing to tell a config mistake from an expired confirmation should read the config instead.
  • It is advisory, not atomic. It reports the state at the instant it is called, so emit on the answer without an await in between. For the case it exists for — an instance that never issued the id — absence cannot become presence, so it cannot go stale in the unsafe direction.

An alternative that was considered and not taken

Making processUserResponse report its own outcome, or forwarding an applied/orphaned event, would be atomic by construction and is arguably the better general shape.

It is not taken here because it does not reach the consumer that motivated this: one that resolves confirmations by emitting on the event emitter never sees a return value, since it is swallowed by the forwarding chain. It is also a larger change to the event contract than this needs to be. Happy to follow up with it if maintainers prefer that direction.

Verification

  • pnpm run pre-push14/14 pass
  • pnpm run build — passes, publint All good!
  • prettier --check — clean on all three source files
  • pnpm run docs:api — regenerated (CI checks for drift). Confirmed the clean branch had no drift beforehand, so everything in docs/api here is attributable to this change; the third doc file is only source-line links shifting by the 3 lines added to types/hitl.ts
  • tsc --noEmit — 575 pre-existing errors on release, 575 with this change, the only diff being line numbers shifting by the added lines. No new errors. (Worth noting separately that typecheck does not currently pass on release.)
  • Both methods and the type member confirmed present in the built dist with correct .d.ts signatures

No changeset: release.yml runs semantic-release, so the feat(hitl): type drives the minor bump. .changeset/ appears vestigial.

Summary by CodeRabbit

  • New Features

    • Added APIs to check whether a human-in-the-loop confirmation is still pending.
    • Added support for checking pending confirmations through both the HITL manager and the main NeuroLink interface.
    • Clarified that pending confirmations are held in memory and may no longer be available after rebuilding an instance.
  • Documentation

    • Updated API references, examples, and source links for the new methods and related HITL types.

A HITL confirmation's pending entry holds the `resolve`/`reject` of the suspended
tool call, so it exists only in the memory of the manager that issued it. Nothing
outside that manager could ask whether it is still there.

That gap is not academic, because emitting a response is not proof the decision
landed. `setupHITLEventForwarding` installs a permanent listener for
`hitl:confirmation-response` at construction, so `emitter.emit(...)` reports a
listener was invoked whether or not anything is waiting. A consumer that rebuilds
a session from persisted state constructs a NEW NeuroLink instance whose pending
set is empty; the emit is accepted, `processUserResponse` logs "No pending
confirmation found for ID" and returns, and the consumer has no way to know. The
case that motivated this had a merchant told a gated write had been approved when
it never ran.

Adds `HITLManager.hasPendingConfirmation(confirmationId)` beside the existing
`getPendingCount()`, and `NeuroLink.hasPendingHITLConfirmation(confirmationId)`
so consumers holding the facade can reach it — `hitlManager` is private, so the
manager's own accessors were unreachable from outside.

The predicate is also added to the `HITLManager` contract type in
`types/hitl.ts`. Without it the method would be invisible through
`toolRegistry.getHITLManager()` and `externalServerManager.getHITLManager()`,
which are both typed against that type rather than the class.

Deliberate limits, documented on the method rather than left to be discovered:

- It returns false both when HITL was never configured and when the id is
  unknown or already settled. Both mean "emitting here achieves nothing", which
  is the question being asked; a caller needing to tell a config mistake from an
  expired confirmation should read the config instead.
- It is advisory, not atomic — it reports the state at the instant it is called,
  so callers should emit on the answer without an `await` in between. For the
  case it exists for, an instance that never issued the id, absence cannot become
  presence, so it cannot go stale in the unsafe direction.

An atomic alternative — making `processUserResponse` report its own outcome, or
forwarding an applied/orphaned event — was considered and not taken here. It does
not reach a consumer that resolves confirmations by emitting on the event
emitter, since the return value is swallowed by the forwarding chain; and it is a
larger change to the event contract than this needs to be.

docs/api regenerated, as CI checks it for drift.
Copilot AI lite review requested due to automatic review settings August 28, 2026 22:47
@github-actions

Copy link
Copy Markdown
Contributor

✅ Single Commit Policy - COMPLIANT

Status: Policy requirements met • 1 commit • Valid format • Ready for merge

📊 View validation details

📝 Commit Details

  • Hash: d09e79390c00f26d34f8601b27185deaf88c8a3a
  • Message: feat(hitl): expose whether a confirmation is still pending
  • Author: Sachin Sharma

✅ Validation Results

  • Single commit requirement met
  • No merge commits in branch
  • Semantic commit message format verified
  • Ready for squash merge to release branch

🤖 Automated validation by NeuroLink Single Commit Enforcement

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9720248e-5f92-4dc5-bae3-4418ca810b2a

📥 Commits

Reviewing files that changed from the base of the PR and between 4f9b452 and d09e793.

📒 Files selected for processing (6)
  • docs/api/classes/HITLManager.md
  • docs/api/classes/NeuroLink.md
  • docs/api/type-aliases/HITLExecutionState.md
  • src/lib/hitl/hitlManager.ts
  • src/lib/neurolink.ts
  • src/lib/types/hitl.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The change adds pending HITL confirmation checks to HITLManager and NeuroLink. It updates the public type declaration and API documentation, including shifted source references.

Changes

HITL pending confirmation checks

Layer / File(s) Summary
Pending confirmation API implementation
src/lib/types/hitl.ts, src/lib/hitl/hitlManager.ts, src/lib/neurolink.ts
The HITL manager exposes hasPendingConfirmation. NeuroLink exposes hasPendingHITLConfirmation and returns false when HITL is unavailable or the confirmation is not pending.
API reference updates
docs/api/classes/HITLManager.md, docs/api/classes/NeuroLink.md, docs/api/type-aliases/HITLExecutionState.md
The documentation describes the new methods and updates generated source-location links.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to d09e7

This change exposes whether a HITL confirmation is still pending without altering confirmation execution behavior. The localized API additions are merge-ready after normal checks, with no actionable merge-blocking risk remaining.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: exposing whether a HITL confirmation remains pending.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2 files. (4 skipped: 3 unsupported, 1 too large.)

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hitl-pending-confirmation-accessor

Warning

Some tools did not complete. Review the errors below.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/lib/hitl/hitlManager.ts

Parsing error: Unable to parse the specified 'tsconfig' file. Ensure it's correct and has valid syntax.

error TS5012: Cannot read file '/.svelte-kit/tsconfig.json': ENOENT: no such file or directory, open '/.svelte-kit/tsconfig.json'.

src/lib/neurolink.ts

ESLint skipped: the matched ESLint configuration already failed (missing-dependency).

src/lib/types/hitl.ts

ESLint skipped: the matched ESLint configuration already failed (missing-dependency).


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Documentation Validation Results

🚀 Documentation validation passed!

Check Status Result
Frontmatter Validation Passed
TypeScript Check Passed
Build Passed
Link Validation Passed

📦 Build artifact uploaded successfully. Ready for deployment preview.

Commit: 2b5c5eade41d87571379a28e32640aa38088a713 | Workflow: View logs

@murdore
murdore merged commit e311f38 into release Aug 29, 2026
28 of 29 checks passed
@murdore
murdore deleted the feat/hitl-pending-confirmation-accessor branch August 29, 2026 03:45
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 12.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants