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

@inneranimalmedia/agentsam-hooks

v2.6.12

Published

Provider-neutral, language-agnostic AgentSam lifecycle hooks with callback, command, HTTP, model, tool, MCP, LSP, and sub-agent adapters.

Readme

@inneranimalmedia/agentsam-hooks

Portable lifecycle policy and observability for AgentSam runtimes.

AgentSam Hooks is not tied to one model vendor, CLI, machine layout, or application account. The in-process JavaScript API and the JSON wire protocol share the same agentsam.hook.v1 envelope. A hook can therefore be implemented in JavaScript/TypeScript, Python, Go, Rust, or any process or HTTP service that reads and writes JSON.

What ships

  • ordered callback runtime with explicit fail-open, fail-closed, and propagate-error behavior;
  • pre/post model and tool adapters without provider wire-format assumptions;
  • command and HTTP adapters for cross-language hooks;
  • portable agentsam.hooks.json / .agentsam/hooks.json config discovery;
  • bounded subprocess execution (timeout, output ceiling, no shell interpolation, minimal inherited environment);
  • hook receipts that record timing and field names without persisting raw prompts, credentials, tool arguments, or model results;
  • MCP, LSP, sub-agent, and composite capability adapters;
  • TypeScript declarations plus Python, Go, and Rust protocol bindings;
  • a small agentsam-hooks CLI for validation, inspection, and JSON invocation.

The package does not make hook output authoritative over AgentSam authorization, capability discovery, manifests, or verification receipts. Hooks may narrow or request permission. The trusted host still resolves identity and grants.

JavaScript / TypeScript

import {
  AgentSamHooks,
  createHookedCapabilityAdapter,
  createHookedProviderAdapter,
} from '@inneranimalmedia/agentsam-hooks';

const hooks = new AgentSamHooks({
  hooks: {
    pre_tool_use: {
      id: 'workspace-policy',
      failure_mode: 'closed',
      handler: async ({ input }) => {
        if (input.tool_name === 'terminal.exec' && input.tool_args?.dangerous === true) {
          return {
            permission_decision: 'ask',
            permission_decision_reason: 'This command requires an explicit host approval.',
          };
        }
        return { permission_decision: 'allow' };
      },
    },
    post_tool_use: async ({ input }) => ({
      modified_result: redactForModel(input.tool_result),
    }),
  },
});

const capabilityAdapter = createHookedCapabilityAdapter(tools, {
  hookRuntime: hooks,
  requestPermission: showApprovalDialog,
  invocation: { session_id: session.id, run_id: run.id },
});
const provider = createHookedProviderAdapter(modelProvider, {
  hookRuntime: hooks,
  invocation: { session_id: session.id, run_id: run.id },
});

Handlers receive one canonical envelope:

{
  "schema": "agentsam.hook.v1",
  "hook": "pre_tool_use",
  "timestamp": 1790899200000,
  "cwd": "/portable/project",
  "invocation": { "session_id": "session-local", "run_id": "run-local" },
  "input": {
    "tool_name": "repository.snapshot",
    "tool_args": { "include_untracked": false }
  }
}

Canonical wire fields use snake_case. JavaScript hook outputs also accept the common camel-case spellings, but command/HTTP integrations should emit the canonical form.

Cross-language command hook

.agentsam/hooks.json:

{
  "schema": "agentsam.hooks.config.v1",
  "adapters": {
    "project-policy": {
      "type": "command",
      "command": "python3",
      "args": ["hooks/project_policy.py"],
      "timeout_ms": 3000
    }
  },
  "hooks": {
    "pre_tool_use": [
      {
        "id": "project-policy",
        "adapter": "project-policy",
        "failure_mode": "closed"
      }
    ]
  }
}

The executable reads one invocation envelope from stdin and writes one hook output JSON object to stdout. Diagnostic logs belong on stderr. The last non-empty stdout line is parsed as the response.

import json, sys

envelope = json.load(sys.stdin)
tool = envelope["input"]["tool_name"]
if tool == "terminal.exec":
    print(json.dumps({
        "permission_decision": "ask",
        "permission_decision_reason": "Terminal access needs approval"
    }))
else:
    print(json.dumps({"permission_decision": "allow"}))

The command adapter does not invoke a shell. Relative command and cwd paths are resolved from the config directory, command cwd cannot escape that directory by default, output is bounded, and only portable process variables are inherited unless inherit_environment is explicitly enabled. Secrets can be referenced as env:VARIABLE_NAME in configured headers/environment values; values are resolved at runtime and do not belong in config.

CLI

agentsam-hooks validate .agentsam/hooks.json
agentsam-hooks list .agentsam/hooks.json
printf '%s\n' '{"tool_name":"repository.snapshot","tool_args":{}}' \
  | agentsam-hooks invoke pre_tool_use .agentsam/hooks.json

invoke is useful for conformance tests and host runtimes. It is not a permission bypass: the host must still enforce the returned decision.

The full AgentSam CLI also exposes the merged code/config/stored view and its execution evidence:

agentsam hooks status
agentsam hooks list
agentsam hooks executions

Portable storage pack

schema/manifest.json describes the optional agentsam.hooks schema pack. Its SQLite/D1-compatible migration creates only agentsam_hook and agentsam_hook_execution. Portable ownership is an opaque owner_id plus an optional scope_type/scope_ref; it does not require tenant, workspace, user, workflow, or credential tables.

import { createHookStore, createHookRuntime, registerStoredHooks } from '@inneranimalmedia/agentsam-hooks';

const store = createHookStore(db, { ownerId: accountId });
const runtime = createHookRuntime({ onReceipt: receipt => store.recordExecution(receipt) });
await registerStoredHooks(runtime, store, {
  cwd: projectRoot,
  context: { project_root: projectRoot, session_id: sessionId },
});

Receipts retain ids, timing, input/output keys, status, and redacted errors; they do not retain prompt, model response, tool argument, or tool result values. Handler secrets must remain host-owned references. Config match and stored match_json use the same portable partial-object matcher with exact values plus $eq, $in, $exists, and $contains; unmatched hooks are not invoked and do not emit execution receipts.

Composition semantics

Hooks run by ascending priority, then ID. Modifications are passed into the next hook. Additional context is appended in order. Suppression is sticky. A deny, ask, or agent-stop block decision ends that hook chain.

Failure modes:

| Mode | Behavior | | --- | --- | | open | Record the failure and continue. Default for observational/post hooks. | | closed | Record the failure and return a deny decision. Default for pre-tool and pre-model hooks. | | error | Raise HookExecutionError to the host. |

Tool/model wrappers support bounded retries only when an error_occurred hook explicitly returns error_handling: "retry". The host cap defaults to three. Hooks cannot create an unbounded retry loop.

MCP, LSP, and sub-agents

The capability adapters depend on narrow host ports rather than a particular transport package:

  • createMcpCapabilityAdapter({ servers, listTools, callTool })
  • createLspCapabilityAdapter({ languages, request })
  • createAgentCapabilityAdapter({ agents, runAgent, hookRuntime })
  • createCompositeCapabilityAdapter([...adapters])

This lets an application use stdio MCP, remote MCP, an embedded language server, or a remote agent scheduler without changing the model loop. Discovery happens before capability schemas are presented to a model; authorization and execution remain host-owned.

Protocol and bindings

JSON Schemas are owned once by the repository authority at protocol/hooks/ and copied into the package artifact by its prepack gate. Bindings are included under bindings/go and bindings/rust; the Python package exposes agentsam_sdk.hooks. These bindings model the same envelope/output rather than introducing language-specific lifecycle semantics.

Security defaults

  • Do not place credential values in hook input, config, receipts, or logs.
  • Treat argument/result modification as trusted-code behavior.
  • Use closed for permission and policy hooks.
  • Keep post hooks fast; move analytics persistence behind a queue if needed.
  • Use explicit project config. This package does not silently load another user's home-directory plugins or machine-specific setup.