Skip to main content

Built-in Plugins

Subnaut ships a small set of plugins bundled with the repository. They live under <repo>/plugins/<name>/ and load automatically alongside user-installed plugins in ~/.subnaut/plugins/. They use the same plugin surface as third-party plugins — hooks, tools, slash commands — just maintained in-tree.

See the Plugins page for the general plugin system, and Build a Subnaut Plugin to write your own.

How discovery works

The PluginManager scans four sources, in order:

  1. Bundled<repo>/plugins/<name>/ (what this page documents)
  2. User~/.subnaut/plugins/<name>/
  3. Project./.subnaut/plugins/<name>/ (requires SUBNAUT_ENABLE_PROJECT_PLUGINS=1)
  4. Pip entry pointssubnaut_agent.plugins

On name collision, later sources win — a user plugin named disk-cleanup would replace the bundled one.

plugins/memory/ and plugins/context_engine/ are deliberately excluded from bundled scanning. Those directories use their own discovery paths because memory providers and context engines are single-select providers configured through subnaut memory setup / context.engine in config.

Bundled plugins are opt-in

Bundled plugins ship disabled. Discovery finds them (they appear in subnaut plugins list and the interactive subnaut plugins UI), but none load until you explicitly enable them:

subnaut plugins enable disk-cleanup

Or via ~/.subnaut/config.yaml:

plugins:
enabled:
- disk-cleanup

This is the same mechanism user-installed plugins use. Bundled plugins are never auto-enabled — not on fresh install, not for existing users upgrading to a newer Subnaut. You always opt in explicitly.

To turn a bundled plugin off again:

subnaut plugins disable disk-cleanup
# or: remove it from plugins.enabled in config.yaml

Currently shipped

The repo ships these bundled plugins under plugins/. All are opt-in — enable them via subnaut plugins enable <name>.

PluginKindPurpose
disk-cleanuphooks + slash commandAuto-track ephemeral files and clean them on session end
security-guidancehooksPattern-match dangerous code on write_file/patch and append a security warning (or block) — 25 rules (Apache-2.0 fork of Anthropic's claude-plugins-official patterns)
observability/langfusehooksTrace turns / LLM calls / tools to Langfuse
teams_pipelinestandaloneMicrosoft Teams meeting pipeline — Graph-backed, transcript-first meeting summaries
spotifybackend (7 tools)Native Spotify playback, queue, search, playlists, albums, library
google_meetstandaloneJoin Meet calls, live-caption transcription, optional realtime duplex audio
image_gen/openaiimage backendOpenAI gpt-image-2 image generation backend (alternative to FAL)
image_gen/openai-codeximage backendOpenAI image generation via Codex OAuth
image_gen/xaiimage backendxAI grok-2-image backend
kanban/dashboardbackend APIREST + WebSocket backend for the desktop app's Kanban view of the multi-agent dispatcher — tasks, comments, fan-out, board switching. See Kanban Multi-Agent.

Memory providers (plugins/memory/*) and context engines (plugins/context_engine/*) are listed separately on Memory Providers — they're managed through subnaut memory and subnaut plugins respectively. The full per-plugin detail for the two long-running hooks-based plugins follows.

disk-cleanup

Auto-tracks and removes ephemeral files created during sessions — test scripts, temp outputs, cron logs, stale chrome profiles — without requiring the agent to remember to call a tool.

How it works:

HookBehaviour
post_tool_callWhen write_file / terminal / patch creates a file matching test_*, tmp_*, or *.test.* inside SUBNAUT_HOME or /tmp/subnaut-*, track it silently as test / temp / cron-output.
on_session_endIf any test files were auto-tracked during the turn, run the safe quick cleanup and log a one-line summary. Stays silent otherwise.

Deletion rules:

CategoryThresholdConfirmation
testevery session endNever
temp>7 days since trackedNever
cron-output>14 days since trackedNever
empty dirs under SUBNAUT_HOMEalwaysNever
research>30 days, beyond 10 newestAlways (deep only)
chrome-profile>14 days since trackedAlways (deep only)
files >500 MBnever autoAlways (deep only)

Slash command/disk-cleanup available in both CLI and gateway sessions:

/disk-cleanup status # breakdown + top-10 largest
/disk-cleanup dry-run # preview without deleting
/disk-cleanup quick # run safe cleanup now
/disk-cleanup deep # quick + list items needing confirmation
/disk-cleanup track <path> <category> # manual tracking
/disk-cleanup forget <path> # stop tracking (does not delete)

State — everything lives at $SUBNAUT_HOME/disk-cleanup/:

FileContents
tracked.jsonTracked paths with category, size, and timestamp
tracked.json.bakAtomic-write backup of the above
cleanup.logAppend-only audit trail of every track / skip / reject / delete

Safety — cleanup only ever touches paths under SUBNAUT_HOME or /tmp/subnaut-*. Windows mounts (/mnt/c/...) are rejected. Well-known top-level state dirs (logs/, memories/, sessions/, cron/, cache/, skills/, plugins/, disk-cleanup/ itself) are never removed even when empty — a fresh install does not get gutted on first session end.

Enabling: subnaut plugins enable disk-cleanup (or check the box in subnaut plugins).

Disabling again: subnaut plugins disable disk-cleanup.

security-guidance

Fast pattern-matched security warnings on file writes. When the agent's write_file / patch / skill_manage calls carry content matching a known-dangerous code pattern — pickle.load, yaml.load without SafeLoader, eval(, os.system, subprocess(..., shell=True), JS child_process.exec, React dangerouslySetInnerHTML, raw .innerHTML = / .outerHTML = / document.write, Node crypto.createCipher, AES ECB mode, TLS verification disabled, XXE-prone xml.etree / minidom parsers, <script src="//..." > without SRI, torch.load without weights_only=True, GitHub Actions ${{ github.event.* }} injection — the plugin appends a ⚠️ Security guidance block to the tool's result.

The file is still written. The model reads the warning in the next turn's tool message and can either fix the code or document why the construct is safe in this context. Pattern matching has a non-trivial false-positive rate, which is why warn (not block) is the default.

Coverage: 25 rules total, covering unsafe deserialization, command injection, XSS sinks, crypto footguns, XXE, supply-chain (SRI), and CI/CD workflow injection. The pattern data is a verbatim Apache-2.0 fork of Anthropic's claude-plugins-official — see the plugin's LICENSE and NOTICE files for attribution.

Modes:

Env varEffect
(unset)warn mode (default) — file is written, warning appended to result
SECURITY_GUIDANCE_BLOCK=1block mode — write refused, warning returned as the block reason
SECURITY_GUIDANCE_DISABLE=1kill switch — plugin loads but does nothing

Enabling: subnaut plugins enable security-guidance (or check the box in subnaut plugins).

Disabling again: subnaut plugins disable security-guidance.

What it does not do (yet): the upstream Anthropic plugin has two more layers — an LLM diff review on each agent turn that touched files, and an agentic commit-time review that traces data flow across files. Neither is ported. The agent can already run those reviews on demand via delegate_task.

observability/langfuse

Traces Subnaut turns, LLM calls, and tool invocations to Langfuse — an open-source LLM observability platform. One span per turn, one generation per API call, one tool observation per tool call. Usage totals, per-type token counts, and cost estimates come out of Subnaut' canonical agent.usage_pricing numbers, so the Langfuse dashboard sees the same breakdown (input / output / cache_read_input_tokens / cache_creation_input_tokens / reasoning_tokens) that appears in subnaut logs.

The plugin is fail-open: no SDK installed, no credentials, or a transient Langfuse error — all turn into a silent no-op in the hook. The agent loop is never impacted.

Setup (interactive — recommended):

subnaut tools # → Langfuse Observability → Cloud or Self-Hosted

The wizard collects your keys, pip installs the langfuse SDK, and adds observability/langfuse to plugins.enabled for you. Restart Subnaut and the next turn ships a trace.

Setup (manual):

pip install langfuse
subnaut plugins enable observability/langfuse

Then put the credentials in ~/.subnaut/.env:

SUBNAUT_LANGFUSE_PUBLIC_KEY=pk-lf-...
SUBNAUT_LANGFUSE_SECRET_KEY=sk-lf-...
SUBNAUT_LANGFUSE_BASE_URL=https://cloud.langfuse.com # or your self-hosted URL

How it works:

HookBehaviour
pre_api_request / pre_llm_callOpen (or reuse) a per-turn root span "Subnaut turn". Start a generation child observation for this API call with serialized recent messages as input.
post_api_request / post_llm_callClose the generation, attach usage_details, cost_details, finish_reason, assistant output + tool calls. If no tool calls and non-empty content, close the turn.
pre_tool_callStart a tool child observation with sanitized args.
post_tool_callClose the tool observation with sanitized result. read_file payloads get summarized (head + tail + omitted-line count) so a huge file read stays under SUBNAUT_LANGFUSE_MAX_CHARS.

Session grouping keys off the Subnaut session ID (or task ID for sub-agents) via langfuse.propagate_attributes, so everything in a single subnaut chat session lives under one Langfuse session.

Verify:

subnaut plugins list # observability/langfuse should show "enabled"
subnaut chat -q "hello" # check the Langfuse UI for a "Subnaut turn" trace

Optional tuning (in .env):

VariableDefaultPurpose
SUBNAUT_LANGFUSE_ENVEnvironment tag on traces (production, staging, …)
SUBNAUT_LANGFUSE_RELEASERelease/version tag
SUBNAUT_LANGFUSE_SAMPLE_RATE1.0Sampling rate passed to the SDK (0.0–1.0)
SUBNAUT_LANGFUSE_MAX_CHARS12000Per-field truncation for message content / tool args / tool results
SUBNAUT_LANGFUSE_DEBUGfalseVerbose plugin logging to agent.log

Subnaut-prefixed and standard SDK env vars (LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, LANGFUSE_BASE_URL) are both accepted — Subnaut-prefixed wins when both are set.

Performance: the Langfuse client is cached after the first hook call. If credentials or SDK are missing, that decision is also cached — subsequent hooks fast-return without re-checking env vars or reloading config.

Disabling: subnaut plugins disable observability/langfuse. The plugin module is still discovered, but no module code runs until you re-enable.

NeMo Relay native integration (migration note)

NeMo Relay is no longer a bundled Subnaut plugin. Do not run subnaut plugins enable observability/nemo_relay; Subnaut core now owns the Relay session, turn, LLM, and tool lifecycles.

To opt into Relay middleware or exporters, create a standard Relay plugins.toml, then set SUBNAUT_NEMO_RELAY_PLUGINS_TOML to that file before starting Subnaut. The policy is process-wide for every profile hosted by that Subnaut process. See the NeMo Relay observability configuration for ATOF, ATIF, and OpenTelemetry options.

The old SUBNAUT_NEMO_RELAY_ATOF_* and SUBNAUT_NEMO_RELAY_ATIF_* settings no longer activate exporters. subnaut doctor reports these stale settings when no replacement plugins.toml is selected.

Session-span segmentation (continuous sessions)

Relay exports a span when its scope closes. A continuous gateway session can keep its session span open for days even though each turn span exports normally. Optional segmentation rotates only the session scope at a turn boundary:

gateway:
telemetry:
session_segments:
on_compaction: false # rotate after context compaction
max_turns: 0 # 0 = unlimited; N = turns per segment
KeyDefaultBehavior
on_compactionfalseRotate after compaction completes, at the next turn boundary.
max_turns0Rotate after every N completed turns; 0 disables the cap.

Both defaults preserve one session scope for the full session. Rotated spans retain the same session_id and add subnaut.session.segment plus subnaut.session.segment_reason (compaction or max_turns).

google_meet

Lets the agent join, transcribe, and participate in Google Meet calls — take notes on a meeting, summarize the back-and-forth after, follow up on specific points, and (optionally) speak replies back into the call via TTS.

What it adds:

  • A headless virtual participant that joins a Meet URL using browser automation
  • Live transcription of the meeting audio via the configured STT provider
  • A meet_join / meet_status / meet_transcript / meet_leave / meet_say toolset the agent invokes to join calls, poll the live transcript, and act on what it heard
  • Post-meeting artifacts (transcript, status) saved under ~/.subnaut/workspace/meetings/<meeting_id>/

Setup:

subnaut plugins enable google_meet
subnaut meet setup # preflight: playwright, chromium, auth file
subnaut meet auth # opens a browser to sign into Google and saves session state —
# needs a Google account with Meet access. Host approval may be
# required if the meeting enforces "only invited participants can join".

Usage from chat:

"Join meet.google.com/abc-defg-hij and take notes. After the call, send me a summary with action items."

The agent kicks off the meeting join, streams the transcription back into its context as the call proceeds, and produces a structured summary when the meeting ends (or when you tell it to stop).

When to use it: recurring standups where you want a bot to transcribe + summarize for async attendees; deposition-style interviews where you want structured notes; any case where you'd otherwise need Fireflies / Otter / Grain. When you'd rather not have an AI listening in — don't enable it.

Disabling: subnaut plugins disable google_meet. Any saved transcripts stay in ~/.subnaut/workspace/meetings/ until you remove them.

Adding a bundled plugin

Bundled plugins are written exactly like any other Subnaut plugin — see Build a Subnaut Plugin. The only differences are:

  • Directory lives at <repo>/plugins/<name>/ instead of ~/.subnaut/plugins/<name>/
  • Manifest source is reported as bundled in subnaut plugins list
  • User plugins with the same name override the bundled version

A plugin is a good candidate for bundling when:

  • It has no optional dependencies (or they're already pip install .[all] deps)
  • The behaviour benefits most users and is opt-out rather than opt-in
  • The logic ties into lifecycle hooks that the agent would otherwise have to remember to invoke
  • It complements a core capability without expanding the model-visible tool surface

Counter-examples — things that should stay as user-installable plugins, not bundled: third-party integrations with API keys, niche workflows, large dependency trees, anything that would meaningfully change agent behaviour by default.