Skip to content

Plugin installation

Source: harness/docs/plugins/install.md
Status: Current

Floor: Agent Plugins 1.0 package shape
Delivery: npm install --save --ignore-scripts into the kernel folder
Identity: plugin.json name only
Enablement: {KERNEL}/config.json plugins.<plugin.json name>.enabled

Surfaces after install: plugins/tldr.md.


Rule
Delivery npm package (npm install --save --ignore-scripts into the kernel folder)
plugin.json MUST live at the package root (same root as package.json for npm). No nested .plugin/plugin.json for our client.
Portable layout Unchanged: skills/, mcp.json at that root
Our extension extensions["tools.inhuman"]: plugins[], commands, bots, skills, states, mcp. drivers[] is still parsed and projected onto DriverSpec. The exemplar packs do not declare one. interceptingHooks and notifyingHooks are dropped at parse.
Identity plugin.json name only — enable/disable, list, uninstall target. package.json name is never the plugin id.

Invalid if plugin.json is missing at package root after install → not a plugin (a library).

npm package name is provenance only (where the bits came from). When it differs from the manifest name, UI/CLI must say so explicitly:

installed plugin `plugin-fff` from `@inhuman.tools/plugin-fff`
enabled plugin `plugin-fff` (from `@acme/legacy-fff-pkg`)
uninstall plugin-fff # always plugin.json name

Never list or address a plugin by npm name alone. If two installs would share the same plugin.json name, reject the second install (collision on plugin id).


The kernel folder (--kernel, default ~/.harness) is a quasi-package. It holds package.json (consent), config.json (connections + plugin enablement), kv.json (session KV bag), and secrets.json (connection secrets — never a sync field). KernelReflector projects config.json + kv.json onto the KernelRoot CRDT.

Consent Install-root package.json dependencies — npm materializes the trees
Enablement {KERNEL}/config.json plugins.<plugin.json name>.enabled — omit = enabled; persist only disables
Load The pack loader does not import plugins[]. Host builtins construct InhumanPlugin and compile it with applyPackPlugins. commands → file-driven Scheme at importPath (Client Arrival). bots, states, and mcp are parsed by the behavior resolver and are not dispatched by the session loop.
Drivers drivers[] is still accepted. A row is copied onto DriverSpec when importPath names that plugin’s own package. New behavior does not add a driver. Exterior = connections on builtins (/connect, MCP). ../runtime/drivers.md
List File reflection (package.json + config.json). Not the write target.

Same human / same machine / same kernel identity → same plugin set. Project trees do not fork “which plugins I have.”

Filesystem: package contents live under the kernel’s node_modules (path installs stay symlinks). That path is referenced from package.json + config.json.

Kernel vs session document roles: ../canon/grain.md.


From the kernel folder:

Terminal window
npm install --save --ignore-scripts @inhuman.tools/plugin-fff@0.0.1
npm install --save --ignore-scripts ./plugins/inhuman-plugin-fff
npm uninstall --save @inhuman.tools/plugin-fff
Step Behavior
install npm install --save --ignore-scripts into the kernel folder → kernel package.json gains the dependency. File reflection lists it.
uninstall npm uninstall --save of the manifest dependency. Consent gone; enablement key may linger unused.

Local path install is still npm against the kernel package.json (symlink, live source).

Scope here means which packages are consented on this kernel (add/remove package.json dependencies), not per-repo enable flags.


Enablement is {KERNEL}/config.json. After install the plugin is enabled by default (no plugins key). Disable writes { "enabled": false }; enable deletes the key.

{
"plugins": {
"plugin-fff": { "enabled": false }
}
}
Rule
After install enabled by default (no config.json key)
enable/disable Writes {KERNEL}/config.json plugins — enable deletes the key; disable writes { "enabled": false }
Not A live-document write for enablement (file reflection of config.json)
Not Per-project toggle
Disabled Not loaded into the catalog or as skills; remains consented

kernel package.json dependencies (consent)
→ plugin graph (reachable plugin.json)
→ config.json plugins: (omit = enabled)
→ skills/, mcp.json (Agent Plugins portable paths)
→ host builtins construct InhumanPlugin and applyPackPlugins
tools, actions, mcpServers, loopEnd
→ extensions.tools.inhuman.plugins[] is parsed and not imported
→ extensions.tools.inhuman.commands → file-driven Scheme (Client)
→ bots / states / mcp → behavior resolver (parsed, not dispatched)

interceptingHooks and notifyingHooks are dropped at parse. skills/ is the portable layout; the daemon does not load skills/.

Manifest-unreachable node_modules trees are not plugins.
A consented plugin with a missing tree: broken install (npm install in the kernel).

Installed means trusted. A consented, enabled plugin’s command importPath is loadable. plugins[] is not imported. There is no second JSON inventory of tools, hooks, or actions, and no HostImportWhitelist.

Host builtins produce tools and actions through InhumanPlugin. Behavior programs are guard/* and hook/* in the bot file. An Arrival behavior capability runs them when a test or caller loads them: the forms are macros, and toJS crosses the lambdas. Callback ambient: lifecycle.md.


Project-local plugin enablement No
pip install as first-class No (npm only)
Auto-trust MCP servers on install No — /connect and mcp-server/adopt adopt user-visible servers. Enabling a trusted plugin activates its declared private bundled server bindings only: bound bots may call the intersection of binding names and tools/list. It does not register that server in the user’s general MCP catalog, grant tools to unbound agents, or auto-trust public/external MCP. Private means unavailable to unbound model callers, not invisible to the operator
Running package postinstall No — --ignore-scripts
Identity from package.json name No — plugin.json name only

Spec We do
Package = directory + root plugin.json Yes
Install source undefined We fix: npm (+ local path) into the kernel folder
Client manages install UX npm for consent; config.json for enable/disable
Skills/MCP fixed paths Yes
Client extensions tools.inhuman: plugins[], commands, bots, skills, states, mcp. Hook rows are dropped. drivers[] is still projected

Pointer sketches: plugins/reference/plugin.json.