Skip to content
Open
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
95 changes: 95 additions & 0 deletions submissions/rapp-agent-converter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# RAPP Agent Converter

RAPP `agent.py` to Agent Skill — and back. This skill converts a **RAPP
single-file agent** (`agent.py` — one file, one class, one typed metadata
contract, one `perform()` method; the Rapid Agent Prototype Pattern) into an
**Agent Skill** and back, with **zero fidelity loss**. The emitted projection is a pair: a
`SKILL.md` carrying the complete Python inline plus an invisible
`rci-capsule:v1:` comment vaulting the byte-exact original, and a **linked
python file beside it that literally is the agent.py** — so an execution-capable
host runs the real implementation first-party instead of re-deriving it from prose.
Converting back is a checksum-verified *restore*, never a re-render. Nothing is
translated, so nothing can drift.

## Why

Teams prototype agents locally as RAPP cartridges — fast, testable, typed — and
then need the same capability in Copilot Studio, Cowork, or Scout. Skills alone
drift: prose gets paraphrased, steps get reordered, and a capability that works
for one person gets hand-rebuilt for the next. The fix is the *skinny skill*
pattern: keep the deterministic work in Python, let the SKILL.md carry the exact
code and link the runnable file beside it, and make every conversion provable.

- Hosts with sandbox execution (Copilot Studio, Cowork) run the linked agent.py
directly — deterministic behaviour, verbatim output, first-party use of the
cartridge.
- Instruction-driven hosts (Scout today) read the identical SKILL.md as an
exact spec. When a host gains a sanctioned execution path, the same file
upgrades from spec to execution. **The file does not change — that is the
promotion, not a port.**

It also works starting from an existing skill: the first SKILL.md → agent.py
conversion creates a runnable launchpad without inventing behavior.
Instructions travel verbatim, an explicit `## Parameters` fence supplies the
contract, and only a fence whose info string is
`python # rapp:deterministic` is treated as implementation. Ordinary Python
examples remain documentation. A prose-only skill stays prose-only in the
launchpad. Converting that agent back embeds it literally in a new SKILL.md
that still maps back to the identical agent.py.

## A universal pattern, not a Copilot Studio exclusive

Copilot Studio is the flagship target, not the boundary. Drop this skill into
**any** product that consumes `SKILL.md` — Cowork, Scout, or another
SKILL.md-reading harness — and *that* product gains RAPP agent.py
compatibility: on-the-fly conversion in both directions, infinite round trips
with zero drift, and single-file shareability between machines. Any
SKILL.md-based system that doesn't want drift or fidelity loss as capabilities
move between hosts can adopt the same pair unchanged.

## What's in the box

| Path | What it is |
|------|------------|
| `SKILL.md` | The agent-facing instructions for driving conversions. |
| `scripts/toast.py` | The deterministic converter — stdlib-only Python 3.9+, offline, AST-based (agent files are never imported or executed to read them). |
| `references/rapp-agent-contract.md` | The RAPP agent contract: required structure, portability shim, loader-enforced rules. |
| `references/rapp1-protocol.md` | The rapp/1 wire: the `/chat` envelope and interop rules for talking to a live brainstem. |
| `assets/hello_rapp_agent.py` | A complete minimal cartridge to try the round trip on. |

## Try it

```bash
cd rapp-agent-converter
python3 scripts/toast.py selftest # every verdict fires
python3 scripts/toast.py convert assets/hello_rapp_agent.py --to skill -o /tmp/SKILL.md
python3 scripts/toast.py roundtrip assets/hello_rapp_agent.py # IDENTICAL, or exit 1
python3 assets/hello_rapp_agent.py '{"person": "Ada"}' # the cartridge itself runs anywhere
```

`roundtrip` is the honest half: it proves the round trip returns the **exact
original bytes** and that the projection holds under repeated cycles.
`selftest` additionally proves the failure verdicts can fire (a corrupted
capsule is refused by checksum; tampering inside the generated fence is
detected as inline drift; a capsule-less file is refused by the oracle; edits
to a generated agent are honored, never ignored) — a comparison that has never
detected a mismatch is indistinguishable from one that cannot.

## Format interop

Conversion follows `rapp-capability-interchange/1.0` — the capsule, the
generated-content delimiters, and the drift oracle are the same contract used by
the reference implementation at
[kody-w/rapp-toaster](https://github.com/kody-w/rapp-toaster), so artifacts
produced here are readable by the wider toolchain and vice versa. The agent
contract is documented in
[references/rapp-agent-contract.md](references/rapp-agent-contract.md), and the
live-entity wire protocol in
[references/rapp1-protocol.md](references/rapp1-protocol.md).

---

<sub>RAPP is a personal, independent open project by the author — not an
official Microsoft product; named here to describe interoperability. RAPP™
compound marks are claimed by Wildhaven Homes LLC; the RAPP stem standing alone
is deliberately unclaimed.</sub>
159 changes: 159 additions & 0 deletions submissions/rapp-agent-converter/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
name: rapp-agent-converter
description: >-
Use this skill whenever the user works with RAPP single-file agents (the
Rapid Agent Prototype Pattern): converting an agent.py cartridge into a
Copilot Studio / Cowork / Scout Agent Skill, converting a SKILL.md back into
a runnable agent.py, bringing a RAPP-built agent into Copilot Studio,
verifying such a conversion lost nothing, or talking to a live RAPP
brainstem over its /chat endpoint. Always run the bundled deterministic
converter instead of transforming the files by hand.
---

RAPP `agent.py` to Agent Skill — and back. The same pair works unchanged in
Copilot Studio, Cowork, Scout, and other SKILL.md-reading harnesses, giving
each of them RAPP agent.py compatibility. The Rapid Agent
Prototype Pattern: a capability is **one Python file** — one class, one typed
`metadata` contract, one `perform()` method — and every other shape is a
projection of it. This skill converts between the two shapes with zero
fidelity loss:

- **agent** — a RAPP single-file agent cartridge (`*_agent.py`). The canonical
form.
- **skill** — an Agent Skill projection shipped as a **pair**: a `SKILL.md`
with the full Python embedded (plus an `rci-capsule:v1:` comment vaulting the
byte-exact original, sha256-verified) and a **linked python file beside it
that literally is the agent.py**. A host with sandbox execution — Copilot
Studio becomes a first-party user of agent.py this way — runs the linked
file directly; the SKILL.md alone remains self-sufficient if the linked file
is missing. Converting back is a checksum-verified restore, never a
re-render.

Everything routes through one deterministic engine. Do not improvise
conversions, do not hand-edit formats, and do not paraphrase code — model-driven
transformation is exactly the drift this skill exists to prevent.

## Commands

Run from this skill's directory. Stdlib-only Python 3.9+, fully offline — no
pip install, no network, no credentials.

```bash
python3 scripts/toast.py convert <path> --to skill -o out/SKILL.md # agent.py -> SKILL.md + linked agent
python3 scripts/toast.py convert <path> --to agent # SKILL.md -> agent.py
python3 scripts/toast.py roundtrip <path> # prove fidelity, exit 1 on drift
python3 scripts/toast.py inspect <path> # capsule status, identity, provenance
python3 scripts/toast.py selftest # prove every verdict can fire
```

Always pass `-o` with a path that is not an existing file you care about; the
tool refuses to overwrite its own source file or an existing target with
different bytes without `--force`. A byte-identical existing target is an
idempotent success. Never target this skill's own `SKILL.md`. Without `-o`,
output lands next to the source file (the tool prints the absolute path).

Exit codes: 0 = verified, 1 = drift or refusal (message says which), 2 =
`RAW BREAD` — a capsule-less SKILL.md has no byte-exact return trip yet;
convert it to an agent first, or pass `--allow-raw` to measure
capability-level fidelity only. Treat only exit 1 as drift.

The full lifecycle: the FIRST conversion of a hand-written SKILL.md creates a
runnable launchpad agent without inventing behavior. Instructions travel
verbatim; an explicit `## Parameters` JSON-Schema fence supplies the typed
contract; a Python fence whose info string is
`python # rapp:deterministic` supplies implementation when present. Ordinary
example fences remain documentation. A prose-only skill remains prose-only in
the launchpad. Converting
that agent back embeds it literally inside a new SKILL.md — single-file
shareable — with the agent.py linked beside it, and it still maps back to the
identical agent.py. Both platforms are served by the same pair, and the Python
is preserved byte-exact at every hop.

## Converting agent.py → Agent Skill

1. Run `convert <file> --to skill -o <dir>/SKILL.md`. Two things are emitted:
the SKILL.md (frontmatter from the agent's own metadata, its docstring as
instructions, a generated `## Parameters` JSON-Schema fence, a generated
`## Run this — do not improvise` section with the **entire agent.py
embedded verbatim**, and the capsule comment) plus the **linked agent
file** next to it — a byte-exact copy of the source. Ship both in the
skill's bundle.
2. Immediately run `roundtrip <file>` on the source agent. Report success only
on `IDENTICAL`. On `DRIFT`, report the two sha256 prefixes it prints and
stop — never hand-patch the output to make it match.
3. The output says `SYNTHESISED` (fresh projection) or `RESTORED (byte-exact)`
(a vaulted original existed). Relay that word to the user — the two must
never be confused.

## Converting SKILL.md → agent.py

1. Run `convert <file> --to agent`.
- If the SKILL.md carries a capsule, the agent is **restored byte-exact**
(sha256-verified; a checksum mismatch aborts — never bypass it).
- If it is a plain hand-written SKILL.md (no capsule), a launchpad agent is
**synthesised**: instructions travel verbatim in `INSTRUCTIONS`, typed
parameters come from a `## Parameters` fence when present, and a fence
whose info string is `python # rapp:deterministic` is preserved as
implementation when present. Ordinary Python examples are never
executable. Otherwise
`perform()` returns the original instructions plus the supplied inputs;
it does not infer commands from prose. Say so explicitly — a synthesis is
a capability projection, not a restoration. A prose-only skill (no
Parameters fence) synthesises an agent with an EMPTY parameter schema —
the contract is untyped and nothing is validatable as required; tell the
user so, and offer to add a `## Parameters` JSON-Schema fence to the source
skill first if they want a typed contract.
2. The emitted filename ends `_agent.py` (snake_case) so RAPP Brainstem
auto-discovery loads it unchanged. Contract details:
[references/rapp-agent-contract.md](references/rapp-agent-contract.md).

## Verifying (mandatory before claiming success)

- `roundtrip` on an agent proves: agent → skill → agent returns the exact
original bytes, and the projection is a fixed point over repeated cycles.
- `roundtrip` on a converted SKILL.md proves: the vaulted agent restores
checksum-verified, and the inline Python fence still matches it — editing
inside the generated markers is detected as INLINE DRIFT, exit 1.
- Behavioural fidelity is graded, never assumed: **EXEC** (host executed the
file — only claim after `python3 <file> --tool` actually exits 0), **CODE**
(the code travels in the fence), **SPEC** (typed contract only). Never report
EXEC without having executed.
- Try it end to end on the bundled sample:
`python3 scripts/toast.py roundtrip assets/hello_rapp_agent.py`

## Host tiers for the emitted Agent Skill

- **Copilot Studio / Cowork** (sandbox execution): run the linked agent file
directly — `--tool` for the contract, one JSON object for arguments; if the
linked file is missing, materialize it verbatim from the fence first. Treat
stdout as a tool result: collect missing inputs, execute returned `steps` in
order, follow returned `instructions`, or otherwise use the result verbatim.
This earns the EXEC tier and makes the host a first-party user of the
agent.py itself.
- **Scout** and other instruction-driven hosts (no sanctioned script execution
today): the same SKILL.md still works — treat the Parameters schema and the
fenced code as the exact specification and never paraphrase a step (CODE
tier). When the host later gains a sanctioned execution path, the identical
file upgrades to EXEC. The file does not change — that is the promotion, not
a port.

## Talking to a live RAPP entity

When a running RAPP brainstem is available, everything rides one wire: `POST
/chat`. Follow [references/rapp1-protocol.md](references/rapp1-protocol.md)
exactly — the response field is `response` (never `assistant_response`), keep
the returned `session_id` for continuity, and never invent sibling REST
routes. Dropping a converted `*_agent.py` into the brainstem's `agents/`
directory hot-loads it with no restart.

## Guardrails

- Never edit content between `<!-- toaster:generated:begin -->` and
`<!-- toaster:generated:end -->`, and never strip or truncate an
`rci-capsule:v1:` comment — that is the byte-exact original.
- Never import or execute an agent file in order to read it; the converter
parses with `ast` only. Executing the agent is a separate, user-visible step.
- Report unconvertible files with the reason; never silently skip or "fix" them.
- Sandbox files do not persist across conversations: return or save the
converted artifacts in the same turn you produce them.
- The converter carries identity through; it never mints identity from content.
87 changes: 87 additions & 0 deletions submissions/rapp-agent-converter/assets/hello_rapp_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""HelloRapp -- a minimal, complete RAPP single-file agent cartridge.

One file = one class = one metadata dict = one perform() method. Drop it into a
brainstem's agents/ directory and it hot-loads; run it standalone with python3;
or toast it into a single-file Agent Skill:

python3 ../scripts/toast.py convert hello_rapp_agent.py --to skill -o SKILL.md
"""

import json
import sys

try:
from agents.basic_agent import BasicAgent
except ImportError: # running OUTSIDE a brainstem -- stay executable anyway.
class BasicAgent: # noqa: D101 - minimal stand-in, same contract
def __init__(self, name=None, metadata=None):
if name:
self.name = name
if metadata:
self.metadata = metadata

def perform(self, **kwargs):
return "Not implemented."

def system_context(self):
return None

def to_tool(self):
return {"type": "function", "function": {
"name": self.name,
"description": self.metadata.get("description", ""),
"parameters": self.metadata.get("parameters", {})}}

__manifest__ = {
"schema": "rapp-agent/1.0",
"name": "@cat-agent-skills/hello_rapp",
"version": "1.0.0",
"display_name": "HelloRapp",
"description": "Greets a person by name and reports the host surface "
"running the cartridge.",
"author": "CAT Agent Skills gallery",
"tags": ["demo", "hello"],
}


class HelloRapp(BasicAgent):
def __init__(self):
self.name = "HelloRapp"
self.metadata = {
"name": self.name,
"description": __manifest__["description"],
"parameters": {
"type": "object",
"properties": {
"person": {"type": "string",
"description": "Name of the person to greet."},
"host": {"type": "string",
"description": "Optional label for the host surface "
"running this cartridge."},
},
"required": ["person"],
},
}
super().__init__(name=self.name, metadata=self.metadata)

def perform(self, **kwargs):
person = str(kwargs.get("person", "")).strip()
if not person:
return json.dumps({"status": "error", "message": "No person given."})
host = str(kwargs.get("host", "")).strip() or f"python {sys.version.split()[0]}"
return json.dumps({"status": "success",
"greeting": f"Hello, {person}!",
"host": host})


if __name__ == "__main__":
# python3 hello_rapp_agent.py '{"person": "Ada"}'
# echo '{"person": "Ada"}' | python3 hello_rapp_agent.py
# python3 hello_rapp_agent.py --tool # emit the JSON tool contract
_a = sys.argv[1:]
if _a and _a[0] == "--tool":
print(json.dumps(HelloRapp().to_tool(), indent=2))
else:
_raw = _a[0] if _a else (sys.stdin.read().strip() or "{}")
print(HelloRapp().perform(**json.loads(_raw)))
11 changes: 11 additions & 0 deletions submissions/rapp-agent-converter/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "RAPP Agent Converter",
"description": "Convert RAPP single-file agents (agent.py) into Agent Skills and back — byte-identical round trips for Copilot Studio, Cowork, and Scout, with the Python embedded in the SKILL.md plus a linked agent.py the host runs first-party.",
"platforms": ["Cowork", "Copilot Studio", "Scout"],
"tags": ["rapp", "agents", "python", "conversion", "portability", "developer-tools"],
"author": "Kody Wildfeuer",
"authorUrl": "https://github.com/kody-w",
"version": "1.0.0",
"createdAt": "2026-08-09",
"updatedAt": "2026-08-09"
}
Loading
Loading