Skip to content

Capabilities

A capability is the right to cause a class of side effects. This is where tools and external resources meet.

The gap isn’t raw capability — agents can already reason and speak, and can spin up sandbox instances for code execution and complex work. The remaining gap is credentialed external actions: posting to Slack, commenting on GitHub, sending a webhook.

A tool should remain a record. Invocation remains gated by x. But tools are not enough by themselves. A tool without a target is abstract. A target without a tool is inert.

So the model wants both:

  • tool records: what operation is possible
  • resource records: what external thing it can operate on

A capability is the combination of an agent, a tool, and an allowed resource boundary.

Capability resolution

A capability is not a record type. It is the derived answer to “can this agent use this tool against this resource?” — computed from the intersection of the agent’s permissions on the tool record and the resource record.

If an agent has x on a tool and x on a resource, they can invoke that tool against that resource. The existing permission system is the single authority. No separate capability grant table, no second ACL that can disagree with the first.

The runtime resolves capabilities by querying: for this agent, which tool records do they have x on, and which resource records do they have x on? The intersection is the capability set.

If we later need narrowing constraints (rate limits, time windows, action subsets), those become constraint records attached to the agent-tool or agent-resource permission edge. But start without them — the permission system is already expressive enough for the hourly Discord test.

Capability resolution at activation time

When a DO wakes up to handle an event, it needs to answer “what can I do right now?” That means querying which tool records and resource records this agent has x permission on, then intersecting those sets against what the activation policy requests. This is a hot path — it runs on every activation.

The current HusAgent has this hardcoded. The new version needs a resolveCapabilities(agentId, db) → { tools, resources } function that returns the resolved permission intersection, with secret resolution as a runtime-injected callback.

Instead of per-call permission checks on tools, create a scoped copy of the tool registry when spawning a child agent. The child literally cannot see tools it shouldn’t use. Maps cleanly onto the permission model: when an agent is activated, build its tool set from the tools it has x permission on in the scope chain. No runtime checks needed during the agent loop — the registry is pre-filtered.

Resources

A resource is an external thing an agent can operate on. It has identity in the substrate so activation policies and audit trails can reference it by record ID rather than by opaque config blob.

Examples:

  • a Discord channel
  • a GitHub repository
  • a Linear workspace
  • a PagerDuty service
  • a code repository checkout
  • a durable stream
  • a sandbox runtime

Resources are scoped to houses because that’s the permission boundary that makes sense — a house admin controls which external targets are available within their scope.

Resources do not carry secret references. A Discord channel does not own the Discord credential — many resources may share one credential, and one resource may need several. Credential binding is a separate concern.

See record-types for the resource record schema.

Secrets

Secrets are the one place where “everything is a record” breaks down if taken too literally.

Credentials should be referenced from records, not stored in plaintext in shared records. The system needs a vault or secret store keyed by stable record IDs.

This is not a failure of the primitives. It is an operational boundary. Name it honestly.

A resource record can say which secret it needs. A run can say which secret reference it used. The secret value itself lives elsewhere.

A capability without secret management is theatre.

Secrets are a binding between a credential and the things that need it, not a property of any single record. The binding record says which credential goes where. The vault_key is the pointer — the secret value itself lives in a vault (environment variable, Cloudflare secret, 1Password, whatever the deployment uses).

At activation time, the runtime resolves: for this agent and this resource, which secret-binding records apply? Then it fetches the actual values from the vault. The substrate never holds credentials at rest. The binding record is auditable — you can answer “which agents have access to which credentials” without touching the vault.

Many resources sharing one credential is the common case. One resource needing multiple credentials (OAuth token + webhook secret) means multiple bindings pointing at the same resource. Both are clean.

See record-types for the secret-binding record schema.

Open question

Should secret-binding records enumerate resource_ids and agent_ids explicitly, or should they use the permission system (agent has r on the secret-binding record → can resolve it)? The permission approach is more consistent but means secret access is governed by the same chain-walking as everything else, which might be too permissive for credentials. Explicit enumeration is safer but means a second access-control path. Leaning toward explicit for secrets specifically — credentials deserve a tighter default.