agent-core
The agent runtime: conversations, streams, model providers, tools, MCP, and the chat UI.
Maturity: beta (80 %)
Agent core. One durable agent stream across many model providers, with a sandboxed execute shell, Agent Skills, delegation, approvals, MCP in both directions, workflows, channels, a terminal and limits on every run.
- Confined shells need a Linux host kernel with Landlock support; without it, lower-trust shells are refused rather than run unconfined.
- Workflows and channels run in a single process; a second replica would run every schedule twice.
- Subscription model usage is not metered in money, so money spend caps do not bind it.
- Container terminal sessions end when the app restarts.
@neuralis/agent-core is the runtime kernel of Neuralis. It owns the agentic
LLM loop end to end: model provider selection, conversation assembly and
persistence, tool execution, policy and hook evaluation, and SSE delivery to
the chat UI. It is also the package that loads every other package — the
package loader, the contribution host, and the generic MCP server all live
here, so every capability any package declares flows through agent-core on its
way to the model. That makes it the package protocol's first provider and its
universal consumer at once: the one runtime that actually executes every
element of the contract, whichever of the three paths a package arrived by.
Responsibilities
- Bootstrap the package system. A single entry point builds the kernel registries, loads agent-core first, then loads every other builtin and installed package and runs their lifecycle hooks.
- Run the agent stream. Provider selection, system-prompt assembly
(instructions, rules, skills, the
<packages>overview, the<runtime_stack>block), tool dispatch, usage metering, and persistence. - Aggregate contributions. Tools, prompts, resources, files, hooks, policies, and connectors from every loaded package are merged into one MCP-compatible surface.
- Serve REST routes under
/api/packages/@neuralis/agent-core/*for agents, conversations, streams, interactions, models, tools, files, packages, connectors, MCP servers, usage, logs, context-window usage, and workflows. - Expose the OAuth 2.1 MCP HTTP server for external clients (Claude Desktop, Cursor, ChatGPT, VS Code) — see MCP access.
- Own scheduled execution through the workflow engine.
- Own the interactive terminal — PTY sessions, the WebSocket companion, source-scoped tabs, and the xterm.js widget.
- Own the host-access plane — the opt-in, operator-provisioned path that lets a shell command or a terminal tab run on the host machine instead of the container. See security.
It does not own filesystem indexing (brain-core), admin surfaces (admin), or machine sandboxes (machine-core).
Provided features
The manifest declares the feature vocabulary other layers gate on (see features and access):
| Feature | What it grants |
|---|---|
core.agents | View and manage agents (create, configure, assign). |
core.execute | Start agent streams and run tools — the chat loop, conversations, interactions, context window — and the execute tool itself. A packaged skill's shell scripts need exec.container beside it. |
exec.container | Open a shell inside the app container with the execute tool: the container plane's ENTRY. Granted wherever core.execute is. It is a door, not a bypass — the source's path policy still decides where a command may start. |
core.observe | Read-only introspection: usage, logs view, tools/files catalog, packages overview, MCP servers, connectors, model list. |
core.web | Use the web_search / web_fetch tools (outbound web egress). Storing a fetched page additionally needs drive.write. |
core.logs | Read an agent's per-agent run log under the session project (escalation beyond core.observe; owner/admin by default). agent-core's platform-global system / package logs need platform.audit instead. |
core.connectors | Create, configure, connect, disconnect, enable, disable and delete a project's outbound tool connectors. An escalation beyond core.observe, which keeps the read side (list, health, generated tools). No grant below the admin tier — admin holds it through its enumerated default grant, owner through the '*' wildcard; grantable to any custom role. |
exec.unconfined | Bypass the execute-shell URI-policy gate and the OS sandbox — inside the container, on any container path (owner/admin by default; grantable per role). It opens the container plane by itself, and grants nothing on the host or on a remote source. |
exec.host | Let the execute tool target the host through the operator-provisioned broker. Not a bypass: host commands are OS-confined (the operator's own ceiling file may declare an unconfined single-operator mode — no role can select it) and additionally clamped by the operator ceiling. No grant below the admin tier — admin holds it through its enumerated default grant, owner through the '*' wildcard, and it is grantable to any custom role. The grant alone is insufficient without operator provisioning. |
terminal.read | See and open the terminal widget; the route feature for every terminal/* route (manager and member by default). |
terminal.container | Open the Container Root terminal tab — a session rooted at / that bypasses source scoping but stays inside the container. No grant below the admin tier; grantable to any custom role. |
terminal.native | Open exact host-source terminal tabs through the same broker as exec.host. No grant below the admin tier; grantable to any custom role. |
terminal.supervise | List, attach to, take control of, and terminate every managed terminal in the project. Host supervision also requires terminal.native. No grant below the admin tier; grantable to any custom role. |
core.prompt-polish | Polish composer drafts with a one-shot LLM call (translate + rewrite before sending). |
core.conversation.fork | Branch a continuable fork of a conversation (manual fork + compacted forks). No grant below the admin tier — admin holds it through its enumerated default grant, owner through the '*' wildcard; grantable to any custom role. |
workflow.read | View workflows and runs. |
workflow.write | Create, edit, pause, and archive workflows. |
workflow.dispatch | Run workflows now and cancel runs. |
channels.connect | Connect your own external channel accounts (Telegram, WhatsApp) and approve pairing requests on them. |
mcp.connect | Connect your own external MCP servers: run the OAuth authorize flow and store the resulting tokens in your own user scope. Project/global-scope MCP secrets stay on the admin Credentials surface. |
mcp.sidecar | Activate command-based (stdio) MCP servers to run in a managed sidecar container. Arbitrary code executes in platform infrastructure, so there is no grant below the admin tier — the seeded admin role holds it through its enumerated default grant, owner through the '*' wildcard; grantable to any custom role. |
mcp.apps | Render interactive HTML UIs (MCP Apps, ui:// resources) from connected MCP servers as chat cards. Untrusted external HTML executes in an isolated sandboxed iframe, so there is no grant below the admin tier — the seeded admin role holds it through its enumerated default grant, owner through the '*' wildcard; grantable to any custom role. |
channels.manage | Manage project-scope channel connections and link channel peers to other users (owner/admin by default). |
git.connect | Connect your own git remotes — paste a Personal Access Token or run the one-click OAuth flow (GitHub, GitLab, Bitbucket, Codeberg) — storing the push token in your own user scope. Project/agent-scope git tokens stay on the admin Credentials surface. |
credentials.self | Set, remove, and list your own credentials (catalog-declared and custom ids) in your own user scope from the chat panel's Credentials group. |
Default role grants: manager and member get agents, execute, observe,
web, prompt-polish, terminal.read, channels.connect, mcp.connect,
git.connect, credentials.self, and workflow read/write (managers
additionally workflow.dispatch); viewer gets agents, observe, and
workflow.read only. The seeded owner role holds the '*' wildcard, so it
holds everything. The seeded admin role does not hold the wildcard — it
holds an enumerated list, declared by this package's own manifest, that covers
every feature above plus the escalation-only ones no weaker role receives:
core.logs, core.connectors, exec.unconfined, exec.host,
core.conversation.fork, mcp.sidecar, mcp.apps, channels.manage,
terminal.container, terminal.native and terminal.supervise. Every one of them is grantable to
any custom role.
The two host-plane features — exec.host and terminal.native —
carry that same admin-tier grant and no more, and holding one is still not
enough unless an operator has provisioned the host broker. See
security.
Feature ids name the plane a shell opens on: exec.container, exec.host, and
whatever id an exec-capable source's connector declares for itself (the virtual
desktop's is exec.machine). exec.unconfined and terminal.container mean
"unrestricted inside the container" — both were once called …host, which
overstated them: they never reached the host. Existing projects are migrated
automatically, including custom roles, so a role that held an older id keeps the
equivalent grant.
Cross-package position
Every other package reaches the model through agent-core:
- The package loader discovers each package's directory contract — tools, skills, instructions, rules, agents, docs, commands, hooks — at bootstrap.
- The contribution host aggregates hosted tools, prompts, and resources
and routes
tools/callback to the owning package's lifecycle. - The stream runtime is the single injection boundary: package contributions are filtered by package enable/disable state and by the caller's granted features before anything reaches the system prompt.
- Cross-package calls use typed APIs from
getPackageApi()— agent-core exposes its agent store, conversation service, usage store, and stream runner this way, and itself consumes brain-core's filesystem API. Its per-agentusage/**directory is declared as machine-written metrics, so new data sources exclude the complete accounting directory by default; existing configured source filters remain owner-controlled.
UI and hosted surfaces
The manifest declares three widgets: the Chat widget (frameless, singleton,
opens by default), the Calendar widget — the
workspace surface for workflows (Day/Week/Month/Year
views, a live activity rail, and a template browser; visible only to
workflow.read holders) — and the Terminal
widget (frameless, multi-instance, terminal.read-gated), an xterm.js PTY
session rooted in a connected filesystem source. Calendar and Terminal each ship
a dock companion.
An empty conversation opens on the Package Constellation: the agent's own icon at the center, and every package it can actually use slowly orbiting it live. Packages group into concentric rings by trust path — first-party installs, sandboxed project drops, and source packages discovered in synced sources — ordered with the smallest group innermost; adjacent rings turn in opposite directions. The view is the permission-filtered truth, not an illustration: each package is a distinct colored icon, clicking one opens a breakdown of its contributions (skills, rules, tools, commands, …), commands and resources insert straight into the composer, and each package can be toggled on or off for just that conversation. Disabled nodes dim in place.
The chat composer includes a Prompt Polish control (core.prompt-polish):
one click sends the draft through a one-shot LLM rewrite — translate to a
target language and improve it at three escalating levels: translate (a
faithful translation, nothing else), gentle (translate, then tidy up and
add light structure while keeping your own voice), and full (engineer the
draft into a richer, well-structured prompt — explicit asks, surfaced
requirements and constraints, an output format and success criteria). Pick any
model, or leave it on Auto to use the same model the conversation runs on.
The result replaces the draft as an
editable preview with one-click undo; at full power the rewriter sees the same
permission-filtered package overview the agent would, so it can phrase the
prompt around the tools and skills actually available to you — never ones
your role cannot see. Over the generic MCP
server, agent-core exposes a selected hosted tool set — currently the two
web tools (web_search, web_fetch) — so external MCP clients
can call them directly; prompts and resources stay unexposed.
Consuming external MCP servers
Neuralis is also a spec-compliant MCP client: agents connect OUT to
external MCP servers over streamable HTTP (spec revision 2025-11-25 —
negotiated protocol version, optional session id, resumable server
notifications with reconnect, request cancellation). Add a server in the
chat config panel's Connected MCP section or on the agent's MCP
configuration; its tools join the agent's catalog next to builtin package
tools. OAuth-protected servers use the standard discovery + PKCE flow: the
Authorize button (or the agent using the MCP authorize lane of connect) produces a
consent link that a human opens and completes — tokens are stored encrypted
in your own user scope, resolved scope-exactly, so one member can never
ride another member's authorization.
Not every server speaks OAuth. A remote server can also carry custom
outbound headers — the API-key header, tenant selector or API-version pin
that server expects — in two separate fields. Non-secret literals
(x-api-version: 2026-01) live in the agent's configuration. Secret ones do
not: you declare the header name when adding the server, which creates an
empty credential slot, and the value is typed into a write-only field on
the server row afterwards. The value is stored in the encrypted credential
store, in your own user scope, and is read only at connect time — it is never
written into the agent configuration, never returned by any listing, and never
recorded in the audit trail (which carries header names only). A server whose
slots are not all filled is not connected at all rather than connected
half-authenticated; it stays visible on the MCP servers rail marked
unavailable so the reason is never invisible. Importing a standard
mcp.json follows the same rule: header names become slots, header values are
never imported.
A few header names are reserved because the platform sets them itself —
authorization (that is what the OAuth/API-key ladder above is for),
content-type, accept, the MCP session and protocol-version headers, and
the standard hop-by-hop names. Those are rejected when you add them, rather
than accepted and then silently overwritten on the wire.
Command-based (stdio) servers — the npx/uvx kind from a standard
mcp.json — import as dormant entries: Neuralis never spawns a child
process on the host. A user holding the mcp.sidecar feature can activate
one to run inside a managed sidecar container: a hardened, resource-capped
Docker container (no privileges, dropped capabilities, loopback-only
publishing) runs the command behind a token-guarded HTTP bridge, isolated
per user or shared per project by the server's scope, idle-reaped when
unused. Secret env vars (say a git server's token) are never stored in agent
config — they live in the encrypted credential store and are injected into
the container only at start. Security floors on every connection:
outbound URLs are SSRF-validated and DNS-pinned (including every OAuth
discovery and token endpoint), no Neuralis identity or internal metadata is
ever sent to an external server, in-host stdio servers are refused, and
capabilities the server did not declare are never called. Connecting servers
is governed by the mcp.connect feature (managers and members by default).
MCP Apps — interactive server UIs in chat
Neuralis renders MCP Apps (the ratified ui:// extension of MCP —
MCP-UI and the OpenAI Apps SDK converged): when a connected server's tool
declares a ui:// HTML template, the tool's result appears as a live,
interactive card in chat. Unmodified MCP-Apps / ChatGPT-Apps servers work
as-is — Neuralis announces the standard client capability at connect, speaks
the standard app protocol (initialize handshake, tool input/result push,
app-initiated tool calls), and hosts the app in the spec's isolated-origin
sandbox: the untrusted HTML runs on a throwaway browser origin (a second
published port), never on the app origin — so Web Storage, self-hosted
subresources, and dynamic apps all render while the platform stays fenced
(the sandbox origin serves nothing but the relay shell, and the template's
Content-Security-Policy is injected server-side, connect-src 'none' by
default). An app calling back into its own server's tools goes through the
exact same policy gate as a model-initiated call — hard deny rules first,
then the conversation's guard profile, with an approval footer on the card
when the guard asks. Rendering is governed by the dedicated mcp.apps
feature, which carries no grant below the admin tier — the seeded admin
role holds it through its enumerated default grant, owner through the '*'
wildcard; grantable to any custom role. An owner who wants MCP connections
without arbitrary-HTML rendering revokes mcp.apps from the roles that should
not have it; mcp.connect is a separate, member-granted feature.
Tool audiences follow the spec's visibility rules, fail-closed. A server tool may declare which audiences it is reachable from — the model, the app, or both. An omitted declaration means both; an explicit list is honoured exactly, so an app-only tool never enters the model's catalog and a model-only tool is not callable from the app; and a malformed declaration is reachable from neither rather than having its valid entries salvaged. An app resolves its calls only against the app-visible tools of the one server that owns it — never a cross-server aggregate — so a same-named tool on another server can neither mask it nor be reached through it.
The card is a retained view: once rendered it keeps its browsing context while the conversation scrolls, so an app does not reload or lose state when it leaves the viewport, and during a live turn its first render waits for the turn to settle so it appears once instead of needing a manual refresh. Every operation the view performs — a tool call, a resource read, its teardown — is bound to a short-lived, opaque View lease that the host keeps in memory and never hands to the app; the server derives the caller's scope from the lease and re-checks live access on every call, and an approval taken through the card resolves exactly once. The full boundary is on the security page.
Folder map
The pages in this section mirror agent-core's real package folders, so the docs map one-to-one onto the code:
| Package folder | Doc page | Cross-reference |
|---|---|---|
tools/ (13 JSON schemas) | Tools | package-system tools |
skills/ (16 bundles) | Skills | package-system skills |
workflows/ (3 templates) | Workflows | package-system contributions |
agents/ (7 base subagents) | Agents | contributions |
instructions/, rules/ | Instructions and rules | contributions |
connectors/ (connector lifecycle) | Connectors | package-system connectors |
channels/ (Telegram/WhatsApp) | Channels | — |
src/git/ (git OAuth connect) | Git remotes | — |
providers/ (model catalog) | Providers and models | — |
app/ (chat, calendar, agent) | App surfaces | package-system App surfaces |
src/terminal/, app/terminal/ (PTY backend + xterm.js widget) | Terminal | package-system routes |
| (cross-cutting) | Security | security model |
In this section
API and usage
Calling agent-core: the model-facing tools, the feature-gated HTTP routes, and the typed getPackageApi() surface.
Native tools
The thirteen built-in tools, with a deep dive on execute, delegate, and the web tools.
Shipped skills
The first-party SKILL.md bundles: management, execution patterns, and research.
Workflows
Scheduled and queued agent work: triggers, the run queue, and creator identity.
Agents
The seven base subagents and how delegate spawns agent runtimes.
Instructions and rules
The system-prompt contributions: the kernel instruction, hygiene, and tool discipline.
Connectors
The connector lifecycle: OpenAPI and subprocess connectors, configure, connect, reconnect.
Channels
Telegram and WhatsApp ingress, pairing, and per-connection credential scope.
Git remotes
Connect a git remote: one-click OAuth, per-user push tokens, and the OAuth App credential.
Bring your own keys
Self-service, user-scoped BYOK: set your own API keys and secrets from the chat config panel.
Providers and models
The multi-provider model layer: catalog, thinking controls, custom endpoints, credential scoping.
App surfaces
The chat, agent, calendar, and terminal widgets contributed to the workspace.
Terminal
The PTY terminal widget: managed sessions, source-scoped tabs, supervision, and container/host planes.
Security
The shell sandbox, URI-policy gating, env sanitization, host access, and the MCP boundary.