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

@borgee/agents-host

v0.2.29

Published

Minimal local agents host: connects local Claude/Codex/Copilot runtimes to a Borgee agent over @borgee/plugin-sdk

Readme

@borgee/agents-host

Minimal local runtime host for Borgee agents.

  • Standalone env mode keeps the existing one-process / one-agent flow.
  • Local-config mode adds first-step multi-agent local hosting from a host config file plus one config file per agent, with hot reload.

What this is (and isn't)

This package intentionally focuses on the smallest local-hosting loop:

Borgee channel message
  → /ws/plugin (BPP)
  → @borgee/plugin-sdk
  → AgentsHost.handleMessage
  → local claude / codex-acp / copilot runtime
  → BPP reply
  → Borgee channel

In scope:

  • standalone env startup
  • foreground CLI startup for one hosted agent
  • local-config multi-agent startup
  • one isolated AgentsHost per effective agent key
  • per-channel conversation memory via each provider's native session mechanism
  • one draft progress message per turn when a provider emits public progress
  • hot reload for host config and agent file add / update / remove

Out of scope:

  • remote command execution / shell dispatch
  • node provisioning
  • systemd/service installation
  • scheduled / periodic prompts

Running

pnpm --filter @borgee/agents-host dev     # tsx, env-var single-agent mode
pnpm --filter @borgee/agents-host build   # tsc -> dist/
pnpm --filter @borgee/agents-host start   # node dist/index.js, env-var single-agent mode

For the published CLI entry point:

agents-host start <serverUrl> <apiKey> [options]
agents-host start-managed <serverUrl>
agents-host start-managed <serverUrl> <apiKey> [agent-options]
agents-host describe-managed <serverUrl>
agents-host apply-managed <serverUrl> --stdin
agents-host apply-managed <serverUrl> --spec-json <json>
agents-host cleanup-managed <serverUrl> [--purge]
agents-host log <serverUrl> [--lines <n>] [--follow]
agents-host log <serverUrl> --path
agents-host start --config <path-to-host-config> [--debug]
agents-host validate --config <path-to-host-config>
agents-host describe --config <path-to-host-config>
agents-host print-layout --root <dir>
agents-host generate-config --root <dir> --stdin
agents-host generate-config --root <dir> --spec-json <json>

Standalone env mode

Use the existing env-var flow when you want exactly one hosted agent per process.

BORGEE_BASE_URL=https://your-borgee-server \
BORGEE_AGENT_API_KEY=bgr_xxxxxxxx \
BORGEE_AGENT_NAME="My Agent" \
RUNTIME_PROVIDER=claude \
pnpm --filter @borgee/agents-host dev

Codex now ships on the same default hosted-provider surface as Claude and Copilot. Select RUNTIME_PROVIDER=codex directly unless you are explicitly rolling that path back with AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES=codex-provider.

Shipped internal compatibility defaults and rollback controls

The current shipped internal compatibility surface resolves gates in this order:

  1. start from the shipped defaults: claude-provider-v2, copilot-provider-v2, codex-provider, managed-runtime-convergence, and policy-audit-enforcement
  2. add any explicit entries from AGENTS_HOST_INTERNAL_COMPATIBILITY_GATES
  3. subtract any explicit entries from AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES

The shipped default set now also includes connections-state-layer, context-injection, skill-runtime, localhost-gateway, token-binding, collaboration-outcome-model, task-thread-collaboration-contract, and collaboration-capabilities-diagnostics. attention-follow-semantics, collaboration-skill-first, and hosted praestoclaw-provider still remain opt-in.

Managed-runtime settings still preserve the managed rollback rule for connections-state-layer plus token-binding: when managed-runtime-convergence remains enabled, the managed surface forces that pair on; explicitly disabling managed-runtime-convergence removes that pair from the managed surface again unless you also explicitly re-enable them.

Managed runtime precedence stays intentional: detached daemon respawns always reuse the persisted managed-runtime-settings.json snapshot, bare start-managed <serverUrl> and apply-managed treat the caller's current env as an explicit request to update that managed runtime surface, and full start-managed <serverUrl> <apiKey> ... validates the agent upsert against the existing managed-root snapshot before ambient rollback env can reject an otherwise valid existing surface. Legacy managed roots created before managed-runtime-settings.json existed need one bare agents-host start-managed <serverUrl> migration pass before full per-agent updates can rely on that snapshot-backed precedence.

Rollback and hardening controls stay explicit:

  • AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES=<comma-list> disables shipped gates for rollback
  • AGENTS_HOST_INTERNAL_PROVIDER_IMPLEMENTATIONS=claude:v1,copilot:v1 forces Claude and/or Copilot back to the v1 adapters while their v2 gates stay enabled
  • AGENTS_HOST_INTERNAL_POLICY_MODE=enforce hardens the shipped policy-audit-enforcement gate beyond its default audit-only mode

Or with the CLI:

agents-host start https://your-borgee-server bgr_xxxxxxxx --provider copilot --debug

Managed daemon bootstrap mode

agents-host start-managed has two forms:

  • agents-host start-managed <serverUrl> <apiKey> [agent-options] bootstraps or reuses the per-server managed daemon, then upserts the requested agent
  • agents-host start-managed <serverUrl> restarts or resumes an already-managed runtime for that server without re-supplying the agent API key or options

Unlike foreground start, both forms exit after the managed daemon is ready, and the full form additionally waits for the requested agent reconcile.

It:

  1. resolves a managed runtime root under ~/.borgee/agents-host/managed/ specific to that normalized serverUrl
  2. reuses the daemon already serving that runtime root when it is alive
  3. otherwise starts one local supervisor daemon for that managed root and waits for it to become ready
  4. for the full <serverUrl> <apiKey> ... form only, upserts the requested agent and waits for that reconcile before reporting success

The short resume form does not auto-seed a new managed runtime. It fails clearly unless that serverUrl already has a persisted managed config with at least one persisted agent entry from an earlier full start-managed <serverUrl> <apiKey> ... bootstrap. Those persisted agents may all currently be disabled; the managed runtime is still considered bootstrapped and resumable in that case.

Each managed runtime root keeps:

  • agents-host.yaml and agents/ as the stable generated local-config entrypoints
  • managed-runtime-settings.json as the persisted managed runtime surface snapshot
  • daemon.log as the detached managed daemon stdout/stderr log file
  • .state/ as the per-agent runtime state base
  • ctl.sock as the local control socket

Detached start-managed daemon launches append both stdout and stderr to <managed-root>/daemon.log. This change does not add rotation or truncation; the daemon keeps appending to the same private log file until tooling or the operator moves or clears it.

This MVP does not migrate legacy ~/.borgee/agents-host/single-agent state into the managed runtime root.

Agent identities generated through start-managed are internal and stable for the same API key within one server runtime root; repeated starts with the same API key replace that generated agent entry instead of appending duplicates.

Example:

agents-host start-managed https://your-borgee-server bgr_xxxxxxxx --provider copilot
agents-host start-managed https://your-borgee-server

Machine-oriented managed-runtime commands

describe-managed and apply-managed are JSON-only commands for machine callers. They operate on the same per-server managed runtime root as start-managed, but they expose full-set managed-runtime state instead of the legacy single-agent upsert flow.

agents-host describe-managed https://your-borgee-server
printf '%s' '{"host":{"borgeeBaseUrl":"https://your-borgee-server"},"agents":[{"key":"cp1","name":"Copilot","apiKey":"bgr_xxx","provider":"copilot"}]}' \
  | agents-host apply-managed https://your-borgee-server --stdin
  • describe-managed prints one JSON object to stdout only:
    • { "ok": true, "present": false }
    • { "ok": true, "present": true, "spec": { ... } }
    • { "ok": false, "error": { "code": "...", "message": "..." } }
  • apply-managed accepts one full-set managed spec, validates that its host.borgeeBaseUrl matches the requested runtime binding, rejects empty full-set specs, then prints one JSON object to stdout only
  • expected managed failures are reported as structured JSON on stdout and exit 0
  • malformed CLI usage still follows the normal usage stderr + non-zero exit path
  • --stdin is preferred because --spec-json exposes the JSON in process arguments

Managed daemon logs

Resolve or stream the managed daemon log for one server runtime directly from the managed runtime root helpers:

agents-host log https://your-borgee-server
agents-host log https://your-borgee-server --lines 0 --follow
agents-host log https://your-borgee-server --path
  • default output prints the last 100 log lines
  • --follow keeps polling for appended content and resumes after truncation or replacement
  • --lines <n> accepts non-negative integers only; 0 suppresses the initial tail before follow mode
  • --path prints only the resolved log path and does not require the file to exist yet

Debug mode

Enable extra host/provider lifecycle logs with either:

agents-host start https://your-borgee-server bgr_xxxxxxxx --provider copilot --debug
AGENTS_HOST_DEBUG=1 agents-host start --config ./agents-host.yaml

Notes:

  • --debug applies to foreground start <serverUrl> <apiKey> and start --config <path>
  • AGENTS_HOST_DEBUG=1 is the env fallback for both the CLI and pnpm --filter @borgee/agents-host dev
  • start-managed intentionally does not accept --debug or AGENTS_HOST_DEBUG=1, because it may reuse an already-running daemon whose log mode is already fixed
  • host-authored debug logs stay secret-safe: they avoid API keys, raw prompt text, raw replies, and other sensitive tokens
  • Copilot child-process stderr is passed through only in debug mode
  • local-config mode prefixes debug logs with the managed agent key so interleaved output stays readable

Environment variables / equivalent single-agent agent options

| Variable | CLI flag | Required | Default | Description | | ---------------------------------- | -------------------------------------- | -------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------ | | BORGEE_BASE_URL | positional <serverUrl> | yes | — | Borgee server base URL | | BORGEE_AGENT_API_KEY | positional <apiKey> | yes | — | Agent API key from the web UI | | AGENTS_HOST_DEBUG | --debug | no | off | Extra host/provider lifecycle logs. Set AGENTS_HOST_DEBUG=1 to enable without the CLI flag. | | BORGEE_AGENT_NAME | --name | no | Assistant | Display name used in prompts | | RUNTIME_PROVIDER | --provider | no | claude | claude, copilot, or codex | | CLAUDE_COMMAND / CLAUDE_ARGS | --claude-command / --claude-args | no | claude / --print --permission-mode bypassPermissions | Local Claude CLI command + args | | CODEX_COMMAND / CODEX_ARGS | --codex-command / --codex-args | no | codex-acp / empty | Local Codex ACP adapter command + args. The default launch resolves the bundled @agentclientprotocol/codex-acp entrypoint. | | COPILOT_COMMAND / COPILOT_ARGS | --copilot-command / --copilot-args | no | copilot / parsed but ignored by ACP runtime | Local Copilot CLI command. The persistent Copilot ACP prototype always launches copilot --acp. | | COPILOT_SESSION_TTL_MINUTES | --copilot-session-ttl-minutes | no | 2880 | Idle TTL for per-channel Copilot ACP sessions |

Local-config mode

Run one supervisor process from a host config file:

agents-host start --config ./agents-host.yaml

Validate the same local-config files without starting agents, the supervisor, watchers, control-plane connections, or provider subprocesses:

agents-host validate --config ./agents-host.yaml

validate supports local-config mode only and reuses the same loadLocalConfigSnapshot() validation path as start --config.

Print the canonical default local-config layout for a root directory without writing files or starting any runtime processes:

agents-host print-layout --root ./runtime-root

This command prints JSON only. It describes the default bootstrap layout:

  • host config file: <root>/agents-host.yaml
  • agents directory: <root>/agents

It does not read an existing host config file, so it does not reflect a custom agentsDir override from agents-host.yaml.

Materialize the canonical default local-config files under that root. Pass the secret-bearing JSON spec on standard input so it is not exposed in process arguments:

printf '%s' '{"host":{"borgeeBaseUrl":"https://borgee.example.com"},"agents":[{"key":"cp1","name":"Copilot","apiKey":"bgr_xxx","provider":"copilot"}]}' \
  | agents-host generate-config --root ./runtime-root --stdin

generate-config is machine-oriented:

  • it accepts one JSON spec object via --stdin; --spec-json remains only as a compatibility input and exposes the JSON in process arguments
  • it always writes the canonical default layout from print-layout
  • it publishes a complete validated generation atomically; a failed generation leaves the prior active set in place
  • concurrent writers for the same managed root are serialized through a root-local lock; a writer waits up to 10 seconds, then fails clearly if the lock remains held
  • it retains the active and immediately previous generations and reader-leased generations, then removes older generated generations (including superseded API keys)
  • if that post-publication pruning fails, the apply remains successful and its JSON summary includes a warnings entry with code PRUNE_SUPERSEDED_GENERATIONS_FAILED
  • it uses a dedicated managed root: <root>/agents-host.yaml and <root>/agents are stable links to the active private generation
  • it replaces stale supported agent config files as part of that atomic full-set publication, never by pruning the active set first
  • it creates and normalizes the managed root and generation directories to mode 0700, and generated host and agent config files to mode 0600
  • it prints a JSON summary without echoing agent API keys

Use a new empty directory as the managed root. generate-config refuses a root whose managed links or generation storage have been replaced with unexpected file types or targets. Existing hand-managed local-config layouts continue to work with start --config and validate --config; they are not converted in place by this command.

The spec shape is:

{
  "host": {
    "borgeeBaseUrl": "https://borgee.example.com",
    "defaults": {
      "claudeCommand": "claude",
      "claudeArgs": ["--print", "--permission-mode", "bypassPermissions"],
      "codexCommand": "codex-acp",
      "codexArgs": [],
      "copilotCommand": "copilot",
      "copilotArgs": ["-s", "--no-color", "--allow-all-tools", "--output-format", "text"],
      "copilotSessionTtlMinutes": 2880
    }
  },
  "agents": [
    {
      "key": "cp1",
      "name": "Copilot",
      "apiKey": "bgr_xxx",
      "provider": "copilot",
      "enabled": true
    }
  ]
}

host.agentsDir is intentionally not accepted here. Canonical generation always targets <root>/agents, and validation is re-run after generation using the same local-config loader as start --config / validate --config. Start the generated layout with agents-host start --config <root>/agents-host.yaml; that stable path follows the active atomically published generation. The loader preserves that stable path for validation output and publication watches, while pinning resolved host and agents-directory paths before reading. Managed snapshots hold a short-lived reader lease until the read finishes, so each snapshot uses one generation even if publication changes current concurrently.

The supervisor loads:

  • one host config file
  • one agent config file per discovered file under the agents directory
  • one isolated AgentsHost per enabled agent key

Supported file types

Host and agent config files may be:

  • *.yaml
  • *.yml
  • *.json

Parsing uses the yaml package for all supported file types.

Recommended layout

packages/agents-host/
  agents-host.yaml
  agents/
    support.yaml
    triage.json

Host config

agentsDir defaults to ./agents relative to the host config file. Absolute paths are also allowed.

borgeeBaseUrl: https://borgee.example.com
agentsDir: ./agents
defaults:
  claudeCommand: claude
  claudeArgs:
    - --print
    - --permission-mode
    - bypassPermissions
  codexCommand: codex-acp
  codexArgs: []
  copilotCommand: copilot
  copilotArgs:
    - -s
    - --no-color
    - --allow-all-tools
    - --output-format
    - text
  copilotSessionTtlMinutes: 2880

Host config fields:

  • borgeeBaseUrl (required)
  • agentsDir (optional, default ./agents relative to the host config file)
  • defaults.claudeCommand
  • defaults.claudeArgs
  • defaults.codexCommand
  • defaults.codexArgs
  • defaults.copilotCommand
  • defaults.copilotArgs
  • defaults.copilotSessionTtlMinutes

Absent host defaults fall back to the same code-level defaults used by the standalone env flow, foreground start, and start-managed.

Agent config

key: support-bot
name: Support Bot
apiKey: bgr_support_xxxxxxxx
provider: claude
enabled: true
claudeArgs:
  - --print
  - --permission-mode
  - bypassPermissions
  - --model
  - sonnet

Agent config fields:

  • key (required): stable identity used for diffing and reload decisions
  • name (required)
  • apiKey (required)
  • provider (required): claude, copilot, or codex (roll back Codex only with AGENTS_HOST_INTERNAL_DISABLED_COMPATIBILITY_GATES=codex-provider)
  • enabled (optional, default true)
  • optional overrides for claudeCommand, claudeArgs, codexCommand, codexArgs, copilotCommand, copilotArgs, and copilotSessionTtlMinutes

Array overrides are replacement, not concatenation. For example, an agent claudeArgs value replaces the host default claudeArgs entirely.

Hot reload behavior

The supervisor watches:

  • the stable host-config parent, so an atomic current publication triggers a reload
  • the resolved host-config generation directory
  • the resolved configured agents directory

Every rename / change event is treated as a reload trigger. Reloads are serialized with initial startup and debounced by at least 300ms. Each reload does a full rescan, validation pass, diff, and apply cycle.

Diff rules:

  • enabled: false agents are excluded before diffing
  • new key → start a new isolated AgentsHost
  • removed key → stop that agent
  • same key + same effective config → no-op
  • same key + changed effective config → stop old runner, then start replacement

Changing an agent key is treated as remove old + add new, so provider-side session state is not preserved across the key change.

If a reload candidate is invalid (malformed host config, malformed agent config, or duplicate agent keys), the supervisor logs the error and keeps the last successfully applied snapshot running.

Conversation memory

Each Borgee channel is mapped 1:1 to a provider-native session while it stays active:

  • Claude: first turn for a channel uses --session-id <uuid>; every turn after uses -r/--resume <uuid>. The host persists the channel→session map in its state root, serializes cross-channel map rewrites, and retries once with a fresh session immediately if Claude reports a stale --resume target.
  • Codex: one persistent @agentclientprotocol/codex-acp subprocess is shared by a single AgentsHost, with one ACP session per Borgee channel. When the current turn has a materialized channel context payload, Codex refreshes a same-directory AGENTS.md project document inside a provider-visible per-channel projection directory and exposes that directory to Codex through ACP additionalDirectories. Ordinary threads keep the shared runtime workspace as the ACP session cwd, while eligible task_assignment threads switch the real ACP session cwd to a hidden local task workspace under <startup-workspace>/.borgee-task-workspaces/<thread>/<task>/; that local writable workspace is explicitly not a claim that the target repository is already checked out there. The host persists the channel→ACP-session map in its state root and attempts ACP session/resume first when the adapter advertises it, otherwise session/load.
  • Copilot: one persistent copilot --acp subprocess is shared by a single AgentsHost, with one ACP session per Borgee channel. Same-channel turns are serialized; different channels keep isolated ACP sessions. Ordinary threads keep the shared runtime workspace cwd, while eligible task_assignment threads switch the real ACP session cwd to that same hidden local task-scoped workspace. The host persists the channel→ACP-session map in its state root and restores same-host continuity with ACP session/resume when available, otherwise session/load plus local replay cleanup on runtimes that advertise only load support.
  • Claude: Claude still keeps one provider-native session mapping per Borgee channel, but unlike the ACP-backed adapters it spawns a fresh CLI process for each turn. Ordinary threads inherit the host runtime cwd; eligible task_assignment threads run the CLI process with the hidden local task-scoped workspace as the process cwd, again without implying that the target repository is already checked out there.

There is no separate transcript/history store on our side. Claude, Codex, and Copilot only persist the native session ids they should try to resume later; cross-host continuity still does not exist in this runtime.

Testing

pnpm --filter @borgee/agents-host exec vitest run --testTimeout=10000
pnpm --filter @borgee/agents-host typecheck
pnpm --filter @borgee/agents-host build