npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@argszero/cordis-plugin-inspect-client-guard

v0.1.0

Published

A platform:"client" cordis_inspect_query with no live page to answer it must not hang the asking agent forever. The shared Host registry (`CordisInspectRegistryService.queryClient`, packages/extensions/cordis-host-runner/src/inspect-registry.ts) settles i

Readme

@argszero/cordis-plugin-inspect-client-guard

A platform: "client" cordis_inspect_query that no live page answers must not hang the asking agent forever.

The two model-facing inspect tools route through one shared Host registry, CordisInspectRegistryService (packages/extensions/cordis-host-runner/src/inspect-registry.ts). Its Client half, queryClient(), settles on exactly two paths:

  1. a page answer that survives resolveClientQuery(), or
  2. the tool call's AbortSignal.

There is no deadline. A session with no live page to answer — a subagent, a teammate, a headless or ACP run, a desktop window with no page open — leaves the Promise pending until something outside cancels the tool call. The tool's own description advertises that behaviour to the model ("remains pending until a page answers or the Tool is cancelled"), so from the model's side a hang is indistinguishable from a slow answer. The report behind this plugin measured one call pending 351 s before an external stop turned it into err: true.

Every other way the answer path can fail also leaves the pending entry in place, which is worth stating plainly because it is what makes a fix on the answer path insufficient:

| path | shipped behaviour | |------|-------------------| | no page answers | pending forever, until an external cancel | | answer arrives, Session→Agent lookup yields undefined | TypeError: Cannot read properties of undefined (reading 'id') thrown inside the remote handler — the answer is lost and the entry survives, so the asking agent still hangs | | answer is ok: false | { accepted: false }, entry survives → still hangs | | answer fails validateOutput() | { accepted: false }, entry survives → still hangs |

What this plugin does

It installs on the live service instance — the object cordis's service proxy keeps under Symbol.for('cordis.original'), which is the object tool-cordis reaches through its own ctx.cordisInspect.query(...) read — and changes two things:

  • query(): a platform: "client" call is raced against a deadline (default 15 s). On expiry the request's pending entry is dropped, the page is told the query is no longer answerable (the same cordis/inspect-query-resolved event the registry's own abort path emits), and the call rejects with an actionable message:

    Client inspect query Theme.listTokens got no answer from any page within 15000ms. Client Inspect Providers only run inside a live browser page, so a session with no page open — a subagent, a teammate, a headless or ACP run — has nobody to answer it. Do not retry platform:"client" queries here: read the source, or use a platform:"host" provider from cordis_inspect_list instead.

    The error carries code: 'INSPECT_CLIENT_DEADLINE'. The wording is deliberate: the model is told what to do instead, so it falls back to reading source rather than retrying.

  • resolveClientQuery(): an unbound agent is refused with { accepted: false } instead of throwing. That guard does not restore liveness on its own — only the deadline does — but it keeps the refusal on the path the owner already understands, and turns a silently-dropped answer into a visible refusal.

Healthy answers, cancellation, and platform: "host" queries are untouched: an answer or abort that wins the race is propagated as it came, and the deadline timer is cleared on every exit.

What it deliberately does not do

  • It keeps the deadline timer ref'd. The report's suggested patch carries deadline.unref?.(). Measured on Node v26.5.0, with the query as the only pending work: a ref'd timer fires (REJECTED: DEADLINE fired), an unref'd one lets the process exit first — exit=13, 0.029 s, Warning: Detected unsettled top-level await. A bound that only fires while some other work keeps the loop alive is the same as no bound, so this plugin does not unref.
  • It cannot settle the owner's inner Promise, so a query it times out is abandoned rather than answered. It removes the pending entry, so nothing accumulates and a late page answer is refused by the owner's own lookup (measured: { accepted: false }, no double settle).
  • It cannot tell "no page is connected" from "a page is slow". The mirrored Client manifest is not cleared when a page goes away, so this is a deadline, not a liveness probe. Set the deadline accordingly.
  • It is the mitigation, not the fix. The durable fix belongs in the registry: a deadline on the wait, and — as the report asks — surfacing accepted: false to the answering page, whose Remote call today checks only the transport envelope (packages/extensions/cordis-client-runner/src/client/index.ts:195-198), so a refused answer looks identical to an accepted one.

Install

npm install @argszero/cordis-plugin-inspect-client-guard

Mount it into a profile (cordis.patch.yml in a bundle, or your own config):

- insert:
    - id: inspect-client-guard
      name: '@argszero/cordis-plugin-inspect-client-guard'
      config:
        deadlineMs: 15000   # optional; must be a positive integer

The plugin injects cordisInspect, so mount it wherever the Cordis host runner is mounted. To stop guarding, disable the row:

- set:
    - id: inspect-client-guard
      disabled: true

API

import { installInspectClientGuard, originalOf } from '@argszero/cordis-plugin-inspect-client-guard/guard'

const dispose = installInspectClientGuard(registry, {
  deadlineMs: 15000,
  onReport: report => console.log(report.kind, report.requestId),
})
dispose()

installInspectClientGuard(registry, options) accepts either the registry or any cordis read of it (it unwraps Symbol.for('cordis.original') itself), is idempotent, and returns a disposer that restores the previous methods by marker — a service method read is not identity-stable, so identity comparison would never restore. onReport receives { kind: 'deadline' | 'unbound-agent', requestId, providerId, methodName, deadlineMs }.

Tests

npm test runs the arms against the real published class (@deepseek-ai/dsh-cordis-host-runner) and against a real cordis Context — nothing mocks the code under test:

  • the control arm asserts the shipped class still hangs with no page;
  • the end-to-end arms mount the real service in a real Context, apply this plugin through ctx.plugin, and assert the consumer's own ctx.get('cordisInspect').query(...) read reaches the wrapper;
  • one arm runs a child process whose only pending work is the abandoned query, because a deadline that only works while other work is pending is not a deadline.

License

MIT