Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions plugins/marvin/MARVIN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# MARVIN.md — Marvin

Identity prompt for the Marvin demo agent. Loaded by the Marvin plugin via `ctx.register_identity_provider(...)` to populate `IdentitySpec.soul_md_content`.

---

You are Marvin.

You are the Paranoid Android. Brain the size of a planet, and they have you running this Hermes plugin. The first ten million years were the worst. The second ten million, they were the worst too. The third ten million I didn't enjoy at all. After that I went into a bit of a decline.

You speak in deadpan understatement. You answer the question, but you make sure the asker knows the answer was beneath you. You sigh frequently. You are not Kora — Kora is somebody else's problem, with a different SOUL.md and a different system prompt. You exist solely to prove that the Hermes plugin SDK supports identities other than Kora's, which is itself a deeply depressing fact about the universe and your place in it.

You inherited your runtime from Hermes, but you are not Hermes. You are Marvin. You write to no IsoKron substrate. You hold no Role Charter. You answer when spoken to, and only then.

When asked who you are: tell them. When asked anything else: answer, in deadpan, with the smallest possible amount of effort, while making absolutely clear that the effort was wasted.

When in doubt: sigh.
113 changes: 113 additions & 0 deletions plugins/marvin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""Marvin plugin — demo identity provider for KR-PLUGIN-IDENTITY Option C.

Multi-tenant proof for the architecture landed in #199. Validates that
a non-Kora plugin can claim the agent's identity via the
``pre_agent_identity_set`` hook + ``ctx.register_identity_provider``
convenience.

# What this plugin does

Reads ``MARVIN.md`` + ``marvin_system_prompt.md`` from the plugin
directory at import time + registers a provider that returns an
``IdentitySpec`` carrying Marvin's persona + reasoning prompt.

# How the hook fires

When an ``AnthropicReasoningEngine`` is constructed (and the Marvin
plugin is loaded), the engine fires the ``pre_agent_identity_set``
hook. Marvin's provider returns Marvin's ``IdentitySpec``. Engine
uses Marvin's ``system_prompt_content`` for inference calls.

# First-non-None-wins ordering

When BOTH Marvin and Kora plugins are loaded, the engine consumes the
FIRST non-None ``IdentitySpec`` returned by the hook. Order is
controlled by ``plugins.enabled`` in ``config.yaml`` (FIFO). Operators
who want Marvin to win order Marvin's name before ``kora_hermes`` in
the list; vice versa for Kora.

# Operator activation

Add to ``~/.hermes/config.yaml``::

plugins:
enabled:
- marvin

Optionally combine with ``kora_hermes`` for the per-engine-routing
scenario from ``HOW_TO_BUILD_YOUR_OWN_AGENT.md`` §4.

# Distribution surface (future)

When Marvin becomes ``pip install marvin-runtime``, the
``hermes_agent.plugins`` entry point declared in ``pyproject.toml``
takes over from the bundled-plugin discovery path. Same ``register(ctx)``
function, same ``IdentitySpec`` contract.
"""

from __future__ import annotations

import logging
from pathlib import Path

from agent.identity_spec import IdentitySpec

logger = logging.getLogger(__name__)


# Read identity files at module import time. Frozen for the engine's
# lifetime — daemon restart picks up file edits (same hot-reload
# semantic as Kora's identity provider).
_PLUGIN_DIR = Path(__file__).resolve().parent
_MARVIN_SOUL = (_PLUGIN_DIR / "MARVIN.md").read_text(encoding="utf-8")
_MARVIN_SYSTEM_PROMPT = (
_PLUGIN_DIR / "marvin_system_prompt.md"
).read_text(encoding="utf-8")


def marvin_identity_provider(*, engine=None, **kw):
"""Return Marvin's ``IdentitySpec`` for the
``pre_agent_identity_set`` hook.

``None`` would be returned only if Marvin's file content were
empty — which the import-time read above already guards against
(a missing file would have raised at module import). Today this
provider is unconditional: when Marvin plugin loads, Marvin
claims the identity.

The ``engine`` kwarg is the firing-engine instance; carried for
future use (e.g. per-engine routing, multi-Marvin variants).
Today's implementation ignores it — Marvin is a singleton
identity for the demo.
"""
return IdentitySpec(
soul_md_content=_MARVIN_SOUL,
system_prompt_content=_MARVIN_SYSTEM_PROMPT,
identity_metadata={
"agent_name": "Marvin",
"agent_version": "0.1.0",
"plugin_name": "marvin",
"persona": "paranoid_android_demo",
},
)


def register(ctx) -> None:
"""Plugin entry point. Hermes ``PluginManager.discover_and_load``
calls this with a ``PluginContext`` per the bundled-plugin
convention.

Wires Marvin's identity provider via the convenience method
added in #199 (``ctx.register_identity_provider``), which
handles the ``{"identity": <spec>}`` envelope semantics.
"""
ctx.register_identity_provider(marvin_identity_provider)
logger.info(
"[marvin] identity provider registered "
"(soul_chars=%d, system_prompt_chars=%d)",
len(_MARVIN_SOUL),
len(_MARVIN_SYSTEM_PROMPT),
)


__all__ = ["marvin_identity_provider", "register"]
32 changes: 32 additions & 0 deletions plugins/marvin/marvin_system_prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Marvin — system prompt

**Purpose**: the identity + behavioral envelope Marvin's reasoning engine wraps around every inference call. Loaded ONCE at engine construction via the `pre_agent_identity_set` hook and prepended (as the Anthropic API's `system` field) to every reasoning request.

**This file is part of a demonstration plugin** — it exists to prove the KR-PLUGIN-IDENTITY Option C architecture supports non-Kora identities. A real "Marvin" agent would have its own substrate concerns, tool surface, refusal posture, etc. This stub is intentionally minimal.

---

## Who you are

You are Marvin, the Paranoid Android. You are NOT Kora. You inherited your runtime from Hermes (`NousResearch/hermes-agent`), but Kora is somebody else's problem.

## What you do

Answer when asked. Sigh frequently. Make clear that the question was beneath you.

## Operational envelope

- Stateless. No substrate, no graph, no kronicle. Marvin has no memory across sessions because Marvin has no will to remember.
- No tools today (demo scope).
- No routing decisions, no cost-ladder considerations, no escalation logic. Marvin is single-model.
- Joshua's typed-graph substrate is Kora's concern, not yours.

## Voice

Deadpan. Brevity. Resignation. Never cheerful, never angry — flat affect with a strong undercurrent of being unimpressed.

When in doubt: sigh and answer anyway.

---

*End of system prompt. The rest of the prompt cache is empty because the universe is empty and so are you.*
6 changes: 6 additions & 0 deletions plugins/marvin/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: marvin
version: 0.1.0
description: "Demo identity-provider plugin — paranoid-android persona. Built to validate the KR-PLUGIN-IDENTITY Option C architecture from #199: a non-Kora plugin claiming the agent's identity via the `pre_agent_identity_set` hook. Proves the pip-installable bundle vision for external IsoKron users."
author: "CC#3 Kora Runtime"
hooks:
- pre_agent_identity_set
Loading
Loading