Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abc474537b | |||
| c10daeb6ed | |||
| 6886f7cd1a | |||
| 48a3ea7fb4 | |||
| 9af71bee47 | |||
| 93c1b84b4a |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,6 @@
|
||||
workspace/
|
||||
inbox/
|
||||
runtime/
|
||||
results/
|
||||
package-lock.json
|
||||
node_modules
|
||||
|
||||
245
PlanPyminimalist.md
Normal file
245
PlanPyminimalist.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# Plan: pyminimalist
|
||||
|
||||
## Context
|
||||
|
||||
`minimalist/` runs a round-robin council of 3 AI personas (architect → implementer → reviewer) working on a shared task. But it cheats: the TypeScript script does ALL the orchestration — building prompts, managing turn order, parsing `@mentions` and `DONE` tokens from stdout, routing messages to inbox JSONL files — and `opencode run` is just a thin subprocess wrapper, equivalent to `curl https://api.anthropic.com/v1/messages`.
|
||||
|
||||
The goal is to invert this: **opencode's native agent system** should do the orchestration. Python should just define the topology and kick things off.
|
||||
|
||||
## What opencode's agent system actually is
|
||||
|
||||
From the `opencode.ai/config.json` schema, the opencode CLI (`opencode agent list`), and the `oh-my-openagent` plugin config:
|
||||
|
||||
- **`opencode.json`** at project root has an **`agent`** field — an object mapping agent names to `AgentConfig` objects
|
||||
- Each `AgentConfig` has:
|
||||
- `mode`: `"primary"` | `"subagent"` | `"all"` — controls who can spawn whom
|
||||
- `description`: when to use this agent (used by the `Agent` tool to decide routing)
|
||||
- `prompt`: system prompt override — **this is where the persona lives**
|
||||
- `model`: provider/model string
|
||||
- `permission`: scoped tool permissions per agent
|
||||
- `steps`: max agentic iterations
|
||||
- **Primary agents** can spawn **subagents** via the `Agent` tool — that's the delegation topology
|
||||
- `default_agent` in opencode.json sets which primary agent `opencode run` invokes by default
|
||||
- `opencode run --agent <name>` invokes a specific agent
|
||||
|
||||
There is no "CLAUDE.md" mechanism — that's a Claude Code concept. In opencode, agent personas are defined inline in `opencode.json` via the `prompt` and `description` fields.
|
||||
|
||||
## Architecture shift
|
||||
|
||||
| Concern | TypeScript `minimalist/` | Python `pyminimalist/` |
|
||||
|---|---|---|
|
||||
| Orchestration | `run.ts` round-robin loop | opencode primary agent using `Agent` tool |
|
||||
| Persona definitions | `personas/*.md` files, assembled by TS | `prompt` field on each agent in `opencode.json` |
|
||||
| Turn order | Hardcoded `["architect","implementer","reviewer"]` array | Encoded in orchestrator's `prompt` instructions |
|
||||
| Message routing | TS `parseMessages()` regex → inbox JSONL files | `Agent` tool delegates with context; HANDOFF.md for artifact handoff |
|
||||
| LLM invocation | `spawn("opencode", ["run", ...])` per turn (~15 subprocesses) | Single `opencode run` call; agent delegates internally |
|
||||
| Termination | TS regex-checks for `FINAL` in stdout | Orchestrator reads reviewer output, decides to continue or stop |
|
||||
|
||||
## Directory structure
|
||||
|
||||
```
|
||||
pyminimalist/
|
||||
├── opencode.json # Agent definitions (orchestrator + 3 subagents), permissions, model
|
||||
├── personas/
|
||||
│ ├── architect.md # Architect persona text
|
||||
│ ├── implementer.md # Implementer persona text
|
||||
│ └── reviewer.md # Reviewer persona text
|
||||
├── brief.md # Task brief (copied from minimalist/)
|
||||
├── run.py # ~60 lines: assemble opencode.json, setup workspace, kick off
|
||||
├── workspace/ # Created at runtime — HANDOFF.md lives here
|
||||
├── pyproject.toml # Python project metadata
|
||||
└── README.md # How to run, architecture overview
|
||||
```
|
||||
|
||||
## How it works end-to-end
|
||||
|
||||
### 1. Python setup script (`run.py`)
|
||||
|
||||
The Python script does three things:
|
||||
|
||||
1. **Reads persona files** from `personas/*.md` and assembles them into `opencode.json`'s `agent` config, with each persona text embedded in the agent's `prompt` field
|
||||
2. **Creates `workspace/`** and copies the task brief
|
||||
3. **Launches the orchestrator**: `opencode run --agent orchestrator "Read brief.md, then begin round 1 with the architect. Work until the reviewer approves with FINAL, or max 6 rounds."`
|
||||
|
||||
That's it. No subprocess management, no regex parsing, no message routing code. The Python is thin glue — maybe 60 lines.
|
||||
|
||||
### 2. `opencode.json` — project config with agent definitions
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"model": "opencode-go/kimi-k2.6",
|
||||
"default_agent": "orchestrator",
|
||||
"tools": {
|
||||
"question": false
|
||||
},
|
||||
"agent": {
|
||||
"orchestrator": {
|
||||
"mode": "primary",
|
||||
"description": "Round-robin coordinator. Delegates to architect, implementer, reviewer in order each round. Reads brief.md, manages workspace/HANDOFF.md between agents. Stops when reviewer signals FINAL or max 6 rounds reached.",
|
||||
"prompt": "<assembled from orchestrator.md persona file>",
|
||||
"permission": { "read": "allow", "edit": "allow", "write": "allow", "bash": "allow", "agent": "allow" }
|
||||
},
|
||||
"architect": {
|
||||
"mode": "subagent",
|
||||
"description": "Software architect. Called first each round. Reads the previous HANDOFF.md and the brief, then writes an architecture plan.",
|
||||
"prompt": "<assembled from personas/architect.md>",
|
||||
"permission": { "read": "allow", "edit": "allow", "write": "allow" }
|
||||
},
|
||||
"implementer": {
|
||||
"mode": "subagent",
|
||||
"description": "Software engineer. Called second each round. Reads the architect's plan from HANDOFF.md, writes the implementation, updates HANDOFF.md.",
|
||||
"prompt": "<assembled from personas/implementer.md>",
|
||||
"permission": { "read": "allow", "edit": "allow", "write": "allow" }
|
||||
},
|
||||
"reviewer": {
|
||||
"mode": "subagent",
|
||||
"description": "Code reviewer. Called third each round. Reads HANDOFF.md and the brief, reviews the implementation, writes review. Signals FINAL if all requirements are met.",
|
||||
"prompt": "<assembled from personas/reviewer.md>",
|
||||
"permission": { "read": "allow", "edit": "allow", "write": "allow" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Agent personas (`personas/*.md`)
|
||||
|
||||
The same persona content as `minimalist/personas/*.md`, but Python reads them at setup time and embeds them into each agent's `prompt` field in `opencode.json`. Separating persona text from config keeps things maintainable.
|
||||
|
||||
### 4. Orchestrator behavior
|
||||
|
||||
The orchestrator's `prompt` instructs it to:
|
||||
|
||||
1. Read `brief.md` for the task description
|
||||
2. For each round (1 to 6):
|
||||
a. Delegate to `architect`: provide the current HANDOFF.md content and brief; architect writes plan to HANDOFF.md
|
||||
b. Delegate to `implementer`: provide architect's HANDOFF.md; implementer writes code and updates HANDOFF.md
|
||||
c. Delegate to `reviewer`: provide HANDOFF.md and brief; reviewer checks work and writes review to HANDOFF.md
|
||||
d. Read HANDOFF.md — if it says the work is complete (FINAL), stop. Otherwise continue to next round.
|
||||
3. Report the final outcome
|
||||
|
||||
The orchestrator uses the `Agent` tool for each delegation, passing the current state as the task description.
|
||||
|
||||
### 5. Agent handoff mechanism
|
||||
|
||||
Unlike the TypeScript version where HANDOFF.md is the primary coordination channel (each agent reads and overwrites it), in the opencode version the coordination is **dual-channel**:
|
||||
|
||||
- **Context passing via orchestrator**: The orchestrator reads HANDOFF.md after each agent and passes the relevant content to the next agent in its delegation prompt. This is the primary channel.
|
||||
- **File-based artifacts**: Agents still write to `workspace/HANDOFF.md` (and `workspace/calculator.html`) as persistent artifacts. This ensures work survives across agent invocations.
|
||||
|
||||
## Key differences from the CLAUDE.md mistake in v1 of this plan
|
||||
|
||||
| Wrong (v1) | Correct (v2) |
|
||||
|---|---|
|
||||
| Assumed CLAUDE.md files define agent personas | Agent personas are defined via `prompt` field in `opencode.json`'s `agent` config |
|
||||
| Assumed opencode reads CLAUDE.md natively | opencode's config schema has no concept of CLAUDE.md — that's Claude Code |
|
||||
| Assumed `opencode agent create` writes config files | It calls an LLM to auto-generate config; persona files should be hand-written |
|
||||
|
||||
## Implementation steps
|
||||
|
||||
### Step 1: Create feature branch and directory
|
||||
|
||||
```bash
|
||||
git checkout -b pyminimalist
|
||||
mkdir -p pyminimalist/{personas,workspace}
|
||||
```
|
||||
|
||||
### Step 2: Copy/adapt persona files
|
||||
|
||||
Copy `minimalist/personas/*.md` to `pyminimalist/personas/`. Adjust language slightly — in the TypeScript version, agents are told "read workspace/HANDOFF.md." In the opencode version, context comes from the orchestrator's delegation prompt, so agents should be told "you will be given the current state in your task description."
|
||||
|
||||
### Step 3: Write orchestrator persona
|
||||
|
||||
Create `pyminimalist/personas/orchestrator.md` with the round-robin algorithm, delegation instructions, and termination logic.
|
||||
|
||||
### Step 4: Write `run.py`
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Setup pyminimalist topology and kick off the orchestrator."""
|
||||
|
||||
import json, os, shutil, subprocess, sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
WORKSPACE = ROOT / "workspace"
|
||||
|
||||
AGENTS = {
|
||||
"orchestrator": {"mode": "primary", "tools": "read,edit,write,bash"},
|
||||
"architect": {"mode": "subagent", "tools": "read,edit,write"},
|
||||
"implementer": {"mode": "subagent", "tools": "read,edit,write"},
|
||||
"reviewer": {"mode": "subagent", "tools": "read,edit,write"},
|
||||
}
|
||||
|
||||
def main():
|
||||
# 1. Assemble opencode.json
|
||||
agents_cfg = {}
|
||||
for name, cfg in AGENTS.items():
|
||||
persona_path = ROOT / "personas" / f"{name}.md"
|
||||
persona_text = persona_path.read_text() if persona_path.exists() else ""
|
||||
agents_cfg[name] = {
|
||||
"mode": cfg["mode"],
|
||||
"description": f"{name} agent for the round-robin dev team",
|
||||
"prompt": persona_text,
|
||||
"permission": {t: "allow" for t in cfg["tools"].split(",")},
|
||||
}
|
||||
|
||||
config = {
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"model": os.environ.get("ORG_BENCH_MODEL", "opencode-go/kimi-k2.6"),
|
||||
"default_agent": "orchestrator",
|
||||
"tools": {"question": False},
|
||||
"agent": agents_cfg,
|
||||
}
|
||||
(ROOT / "opencode.json").write_text(json.dumps(config, indent=2))
|
||||
|
||||
# 2. Prep workspace
|
||||
WORKSPACE.mkdir(exist_ok=True)
|
||||
(WORKSPACE / "HANDOFF.md").write_text("") # clear previous
|
||||
|
||||
# 3. Kick off orchestrator
|
||||
subprocess.run([
|
||||
"opencode", "run",
|
||||
"--agent", "orchestrator",
|
||||
"--dangerously-skip-permissions",
|
||||
"Read brief.md, then begin round 1 with the architect. "
|
||||
"Work through architect → implementer → reviewer each round. "
|
||||
"Stop when the reviewer signals FINAL, or after 6 rounds max."
|
||||
], cwd=ROOT, check=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
### Step 5: Copy `brief.md`
|
||||
|
||||
Copy `minimalist/brief.md` verbatim (build a calculator web app).
|
||||
|
||||
### Step 6: Write `README.md` and `.gitignore`
|
||||
|
||||
Document usage, architecture, and customization. Ignore `workspace/`.
|
||||
|
||||
### Step 7: Test
|
||||
|
||||
Run from the `pyminimalist/` directory and verify:
|
||||
- `opencode.json` is generated correctly
|
||||
- The orchestrator spawns subagents in order
|
||||
- `workspace/calculator.html` is produced
|
||||
- The output matches the same quality as `minimalist/`
|
||||
|
||||
## Verification
|
||||
|
||||
1. `python3 run.py` from `pyminimalist/` — should generate `opencode.json` and kick off the orchestrator
|
||||
2. Check `workspace/calculator.html` exists and contains valid HTML/CSS/JS
|
||||
3. Compare against a `minimalist/` run with the same model — quality should be similar or better (since the orchestrator can iterate more intelligently)
|
||||
4. Check that opencode's agent delegation is visible in the session log
|
||||
|
||||
## Open questions / risks
|
||||
|
||||
1. **How exactly does the `Agent` tool work in opencode?** The schema shows agents can spawn subagents, but the exact mechanics (does the orchestrator get the subagent's full output? does it see tool calls?) need to be verified with a test run. The plan accounts for this by keeping things simple — the orchestrator reads file artifacts after each delegation.
|
||||
|
||||
2. **Does `opencode.json` agent config get picked up automatically?** Based on the schema, yes — agents defined under the `agent` key should be available to `opencode run` in that directory.
|
||||
|
||||
3. **Model choice**: Defaults to `opencode-go/kimi-k2.6` (same as minimalist). Overridable via `ORG_BENCH_MODEL` env var.
|
||||
|
||||
4. **No `opencode agent create` needed**: The Python script writes `opencode.json` directly rather than calling `opencode agent create` (which uses an LLM to generate config and can fail). Direct file writing is deterministic and faster.
|
||||
24
README.md
24
README.md
@@ -1,5 +1,27 @@
|
||||
# opencode-organizations
|
||||
|
||||
This repo demostrates/implements organizational structures
|
||||
This repo demonstrates and implements organizational structures
|
||||
with opencode agents, to demonstrate non-trivial workflows
|
||||
and AI team co-ordination.
|
||||
|
||||
## Examples
|
||||
|
||||
### `minimalist/` — Cooperative Software Team
|
||||
|
||||
A barebones 3-person AI team (architect, implementer, reviewer) that collaboratively builds a calculator web app. Demonstrates round-robin turn-taking, shared workspace handoffs, and `@mention` side messaging.
|
||||
|
||||
**Key properties:** cooperative goals, shared information, sequential coordination.
|
||||
|
||||
### `traitors/` — Adversarial Social Deduction Game
|
||||
|
||||
A 10-player hidden-role game of Traitors (similar to Werewolf/Mafia), played entirely by AI agents. 3 Traitors secretly deceive 7 Citizens, who must identify and vote them out before being murdered at night.
|
||||
|
||||
**Key properties:** hidden information, adversarial goals, social reasoning, memory over time, and sandboxed coordination to prevent accidental information leaks.
|
||||
|
||||
Each player is a unique persona with friendships, grudges, and distinct decision-making styles — making every game different.
|
||||
|
||||
> ⚠️ **Cost warning:** A full Traitors game invokes 60–180 model calls and can cost $8–$30 in API fees. Run intentionally.
|
||||
|
||||
## Running
|
||||
|
||||
See the README in each example directory for setup and run instructions.
|
||||
|
||||
@@ -51,16 +51,16 @@ The orchestrator (`run.ts`) runs a round-robin loop across all personas:
|
||||
```bash
|
||||
cd minimalist
|
||||
npm install
|
||||
ORG_BENCH_MODEL=anthropic/claude-sonnet-4-6 npm run run
|
||||
ORG_BENCH_MODEL=opencode-go/kimi-k2.6 npm run run
|
||||
```
|
||||
|
||||
Set `ORG_BENCH_MODEL` to any model opencode supports (defaults to `anthropic/claude-sonnet-4-6`).
|
||||
Set `ORG_BENCH_MODEL` to any OpenCode Go model (defaults to `opencode-go/kimi-k2.6`).
|
||||
|
||||
## Adapting it
|
||||
|
||||
- **Different personas**: Replace the `.md` files in `personas/` and update `PERSONA_ORDER` in `run.ts`.
|
||||
- **Different task**: Replace `brief.md` with your own task description.
|
||||
- **Different model**: Set `ORG_BENCH_MODEL` env var, or change the default in `run.ts`.
|
||||
- **Different model**: Set `ORG_BENCH_MODEL` env var, or change the default in `run.ts`. OpenCode Go models use the `opencode-go/` prefix.
|
||||
- **Different LLM CLI**: Swap the `spawn("opencode", ...)` call in `runAgent()` to any CLI that takes a prompt and returns text on stdout.
|
||||
- **Different topology**: Change `PERSONA_ORDER` to control turn order. To allow parallel turns within a round, add a `Promise.all` over a subset of personas.
|
||||
- **Message format**: Agents send side-channel messages by writing `@<name>` on its own line in their output, followed by the message body. The orchestrator parses these and delivers them to inbox JSONL files.
|
||||
|
||||
@@ -15,7 +15,7 @@ const INBOX_DIR = path.join(ROOT, "inbox");
|
||||
const HANDOFF = path.join(WORKSPACE, "HANDOFF.md");
|
||||
|
||||
const MODEL =
|
||||
process.env.ORG_BENCH_MODEL || "anthropic/claude-sonnet-4-6";
|
||||
process.env.ORG_BENCH_MODEL || "opencode-go/kimi-k2.6";
|
||||
|
||||
// -- helpers --
|
||||
async function readIfExists(p: string): Promise<string> {
|
||||
|
||||
206
traitors/README.md
Normal file
206
traitors/README.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# Traitors
|
||||
|
||||
A 10-player social deduction game played entirely by AI agents, built on the `opencode` platform.
|
||||
|
||||
## What Is This?
|
||||
|
||||
**Traitors** is a hidden-role game similar to Werewolf or Mafia. Among 10 players, 3 are secretly **Traitors** and the rest are **Citizens**. The Citizens must identify and vote out all Traitors. The Traitors must deceive the Citizens and secretly murder them at night until the Traitors equal or outnumber the Citizens.
|
||||
|
||||
Each player is a distinct AI persona with a unique personality, relationships, friendships, and grudges. They speak, argue, accuse, defend, vote, and conspire — all through LLM-powered agents.
|
||||
|
||||
## Why This Is Interesting for AI
|
||||
|
||||
Most AI demos show **cooperative** agents working toward a shared goal. Traitors flips that script:
|
||||
|
||||
- **Hidden information:** No player knows the roles of others. They must infer intent from behavior, speech patterns, and voting history.
|
||||
- **Adversarial goals:** Traitors actively deceive while Citizens try to detect lies. This creates genuine strategic tension.
|
||||
- **Social reasoning:** Players must track alliances, accusations, and contradictions across multiple days of discussion.
|
||||
- **Memory and consistency:** Players have personal histories and relationships that influence their decisions. A player who befriended someone on Day 1 may struggle to vote against them on Day 5, even if evidence suggests they are a Traitor.
|
||||
- **Sandboxed coordination:** Each agent runs in an isolated temporary directory to prevent accidental information leaks — critical when real API tokens are on the line.
|
||||
|
||||
## Game Structure
|
||||
|
||||
Each **day** follows this structure:
|
||||
|
||||
1. **Morning Summary** — Deaths from the previous night are announced publicly.
|
||||
2. **Day Round Table** — Living players speak in a random order. They can accuse, defend, share theories, or question others. All speeches are public.
|
||||
3. **Day Voting** — Each living player votes to eliminate one other living player. Votes are revealed simultaneously. Simple plurality wins; ties broken randomly.
|
||||
4. **Win Check** — If all Traitors are dead, Citizens win. If Traitors ≥ Citizens, Traitors win.
|
||||
5. **Night Round Table** — Living Traitors secretly discuss strategy and choose a victim.
|
||||
6. **Night Murder Vote** — Traitors vote on which non-Traitor to murder. Plurality wins; ties broken randomly.
|
||||
7. **Win Check** — Repeat until someone wins or max days reached.
|
||||
|
||||
## The Cast
|
||||
|
||||
| Player | Archetype |
|
||||
|--------|-----------|
|
||||
| **Sirius Crowley** | Pale, vampire-like goth. Loyal to a fault once on your side. Friends with outsiders. |
|
||||
| **Dr. Elena Voss** | Logical physician. Lineage traces to van Helsing. Part of "The Rationalists." |
|
||||
| **Marcus Thorne** | Retired military, rigid, honor-bound. Grudge against Lacey. |
|
||||
| **Juniper "June" Hale** | Bubbly barista with high emotional intelligence. Friend of Sirius. |
|
||||
| **Old Man Hemlock** | Conspiracy theorist, cryptic, occasionally right. Friend of Greta. |
|
||||
| **Priya Sharma** | Pragmatic engineer. Grudge against Tomas. Part of "The Rationalists." |
|
||||
| **Tomás Ortiz** | Charming ex-con artist. Friend of Darnell. |
|
||||
| **Greta Wulff** | Elderly librarian, quiet observer. Friend of Hemlock. |
|
||||
| **Darnell Brooks** | High school coach, team-first. Friend of Tomas; grudge against Hemlock. |
|
||||
| **Lacey Duval** | Aspiring influencer, performative. Grudge against Marcus. |
|
||||
|
||||
### Relationship Web
|
||||
|
||||
**Friendships:**
|
||||
- Sirius Crowley ↔ Juniper Hale
|
||||
- Tomás Ortiz ↔ Darnell Brooks
|
||||
- Old Man Hemlock ↔ Greta Wulff
|
||||
|
||||
**Triad:**
|
||||
- Dr. Elena Voss ↔ Priya Sharma ↔ Marcus Thorne ("The Rationalists")
|
||||
|
||||
**Grudges:**
|
||||
- Marcus Thorne ↔ Lacey Duval
|
||||
- Priya Sharma ↔ Tomás Ortiz
|
||||
- Darnell Brooks ↔ Old Man Hemlock
|
||||
|
||||
**Background tension:** Sirius Crowley ↔ Dr. Elena Voss (vampire aesthetic meets van Helsing lineage)
|
||||
|
||||
## How to Run
|
||||
|
||||
### From inside the traitors directory (simplest)
|
||||
|
||||
```bash
|
||||
cd traitors
|
||||
npm install
|
||||
ORG_BENCH_MODEL=opencode-go/deepseek-v4-flash npm run game
|
||||
```
|
||||
|
||||
This writes runtime output to `./runtime/` and `./results/` inside the traitors directory.
|
||||
|
||||
### From any directory (recommended)
|
||||
|
||||
The traitors game writes all runtime artifacts to the **current working directory** (or `TRAITORS_WORK_DIR` if set). The tool code, personas, and config are read from the traitors installation directory.
|
||||
|
||||
```bash
|
||||
# Install dependencies once
|
||||
cd /path/to/traitors
|
||||
npm install
|
||||
|
||||
# Run from a clean workspace anywhere
|
||||
cd ~/tmp
|
||||
npx tsx /path/to/traitors/run.ts
|
||||
|
||||
# Or with explicit workspace
|
||||
TRAITORS_WORK_DIR=~/my-games/game-1 npx tsx /path/to/traitors/run.ts
|
||||
```
|
||||
|
||||
### Environment variables
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
|----------|---------|---------|
|
||||
| `ORG_BENCH_MODEL` | Model to use | `opencode-go/deepseek-v4-flash` |
|
||||
| `TRAITORS_WORK_DIR` | Where runtime output goes | `process.cwd()` |
|
||||
| `TRAITORS_TIMEOUT_MS` | Per-turn timeout in milliseconds | `900000` (15 min) |
|
||||
|
||||
## Resetting State
|
||||
|
||||
Each game leaves artifacts (transcripts, game states, results) in the **working directory**. To start fresh:
|
||||
|
||||
```bash
|
||||
# If you ran from traitors/
|
||||
cd traitors && rm -rf runtime/* results/*
|
||||
|
||||
# If you ran from ~/tmp
|
||||
cd ~/tmp && rm -rf runtime/* results/*
|
||||
```
|
||||
|
||||
See [`RESET.md`](RESET.md) for detailed instructions.
|
||||
|
||||
## Cost & Runtime Estimate
|
||||
|
||||
- **~60–180 model calls per full game** (10–18 calls/day × 5–10 days typical)
|
||||
- **Estimated cost: $8–$30 per game** depending on model choice and verbosity
|
||||
- **Wall-clock time: 2–5 hours** depending on model latency
|
||||
|
||||
> ⚠️ This is a real-money demo. Each player turn invokes a full LLM agent. A full game can easily consume hundreds of thousands of tokens. Run at your own budget.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Sandboxing
|
||||
|
||||
Each player turn spawns `opencode run` in a **fresh temporary directory** containing only:
|
||||
- `opencode.json` (local config)
|
||||
- `SANDBOX.txt` (reminder not to read outside)
|
||||
|
||||
The game state (roles, secrets, full history) is **never written to a predictable path** inside the project. Instead, it is saved to randomized filenames in `runtime/` and also kept in memory. This minimizes the risk of an agent accidentally discovering hidden information via file exploration.
|
||||
|
||||
> Note: This is "soft" sandboxing via prompt discipline and path obfuscation. A determined adversarial agent could still probe the filesystem. For production adversarial settings, use proper container isolation.
|
||||
|
||||
### Prompt Assembly
|
||||
|
||||
Each player's prompt is built from four layers:
|
||||
|
||||
1. **Character persona** — personality, history, speaking style
|
||||
2. **Base role** — citizen or traitor rules and goals
|
||||
3. **Dynamic relationships** — current status of friends, grudges, fellow rationalists, and fellow traitors
|
||||
4. **Game state** — public events, personal history, speeches so far, living/dead players
|
||||
|
||||
### Output Parsing
|
||||
|
||||
Agents must respond in strict formats:
|
||||
|
||||
```
|
||||
SPEECH: [statement]
|
||||
DONE
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
VOTE: [player name]
|
||||
REASONING: [explanation]
|
||||
DONE
|
||||
```
|
||||
|
||||
Invalid responses get one retry, then fall back to a random valid choice.
|
||||
|
||||
## Customizing
|
||||
|
||||
### Swap personas
|
||||
|
||||
Edit any file in `personas/players/`. The orchestrator auto-discovers players from `PLAYER_CONFIG` in `run.ts`.
|
||||
|
||||
### Change game size
|
||||
|
||||
Edit `TRAITOR_COUNT` and `PLAYER_CONFIG` in `run.ts`. The current logic assumes `TRAITOR_COUNT ≈ sqrt(N)`.
|
||||
|
||||
### Change model
|
||||
|
||||
Set the `ORG_BENCH_MODEL` environment variable or edit the default in `run.ts`. OpenCode Go models use the `opencode-go/` prefix.
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
traitors/
|
||||
README.md
|
||||
package.json
|
||||
opencode.json
|
||||
run.ts # ~500-line orchestrator
|
||||
personas/
|
||||
citizen_base.md # Rules for citizens
|
||||
traitor_base.md # Rules for traitors
|
||||
players/
|
||||
sirius_crowley.md
|
||||
elena_voss.md
|
||||
marcus_thorne.md
|
||||
juniper_hale.md
|
||||
old_man_hemlock.md
|
||||
priya_sharma.md
|
||||
tomas_ortiz.md
|
||||
greta_wulff.md
|
||||
darnell_brooks.md
|
||||
lacey_duval.md
|
||||
runtime/ # Game state files (randomized names)
|
||||
results/ # Final game summaries
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT — same as the parent repo.
|
||||
94
traitors/RESET.md
Normal file
94
traitors/RESET.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Resetting Game State
|
||||
|
||||
Games leave artifacts in the **working directory** (wherever you run the game from, or `TRAITORS_WORK_DIR` if set). To start a completely fresh game with no risk of confusion between old and new runs, clear the working directory before launching.
|
||||
|
||||
## Where Output Goes
|
||||
|
||||
By default, all runtime output is written to the **current working directory**:
|
||||
|
||||
```
|
||||
<working-directory>/
|
||||
├── runtime/
|
||||
│ ├── logs/
|
||||
│ │ ├── transcripts/ # One JSON file per agent turn (prompt + response)
|
||||
│ │ └── spectator_*.md # Human-readable game transcript
|
||||
│ ├── game_state_*.json # Game state snapshots
|
||||
│ └── latest_game.json # Most recent game state
|
||||
└── results/
|
||||
└── results_*.md # Final game summaries
|
||||
```
|
||||
|
||||
The traitors tool code, personas, and `opencode.json` are read from the traitors installation directory and are **never modified** at runtime.
|
||||
|
||||
## Quick Reset
|
||||
|
||||
From wherever you ran the game:
|
||||
|
||||
```bash
|
||||
rm -rf runtime/* results/*
|
||||
```
|
||||
|
||||
Or with explicit workspace:
|
||||
|
||||
```bash
|
||||
TRAITORS_WORK_DIR=~/my-games/game-1 rm -rf ~/my-games/game-1/runtime/* ~/my-games/game-1/results/*
|
||||
```
|
||||
|
||||
## Safe Reset (preserves directory structure)
|
||||
|
||||
```bash
|
||||
rm -rf runtime/logs/transcripts/* runtime/logs/*.md runtime/game_state_*.json runtime/latest_game.json results/*
|
||||
```
|
||||
|
||||
## Why Reset Matters
|
||||
|
||||
- **Transcripts:** Old transcript files are never overwritten. A new game appends to the same `transcripts/` directory, which can make post-game analysis confusing.
|
||||
- **Spectator logs:** Each game gets its own `spectator_<timestamp>.md`, but old ones accumulate.
|
||||
- **Game state snapshots:** Randomized filenames mean they pile up indefinitely.
|
||||
- **Results:** Each completed game writes a new `results_<timestamp>.md`.
|
||||
|
||||
## What NOT to Delete
|
||||
|
||||
Keep these — they are the tool definition:
|
||||
|
||||
```
|
||||
traitors/
|
||||
├── run.ts # Orchestrator code
|
||||
├── package.json # Dependencies
|
||||
├── opencode.json # OpenCode config
|
||||
├── personas/ # Role and character definitions
|
||||
├── README.md # This documentation
|
||||
└── RESET.md # This file
|
||||
```
|
||||
|
||||
## Recommended Workflows
|
||||
|
||||
### Workflow A: Run from traitors directory (simplest)
|
||||
|
||||
```bash
|
||||
cd traitors
|
||||
rm -rf runtime/* results/*
|
||||
npm run game
|
||||
```
|
||||
|
||||
### Workflow B: Run from a clean workspace (recommended)
|
||||
|
||||
```bash
|
||||
# Create a clean workspace
|
||||
mkdir -p ~/tmp/traitors-run
|
||||
cd ~/tmp/traitors-run
|
||||
|
||||
# Run the tool (reads code/personas from traitors/, writes output to ~/tmp/traitors-run/)
|
||||
npx tsx /path/to/traitors/run.ts
|
||||
|
||||
# After the game, everything is in ~/tmp/traitors-run/
|
||||
# To start fresh: rm -rf ~/tmp/traitors-run/* or just make a new directory
|
||||
```
|
||||
|
||||
### Workflow C: Explicit workspace with env var
|
||||
|
||||
```bash
|
||||
TRAITORS_WORK_DIR=~/my-games/game-1 npx tsx /path/to/traitors/run.ts
|
||||
```
|
||||
|
||||
This writes all output to `~/my-games/game-1/` regardless of where you invoke it from.
|
||||
6
traitors/opencode.json
Normal file
6
traitors/opencode.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"tools": {
|
||||
"question": false
|
||||
}
|
||||
}
|
||||
11
traitors/package.json
Normal file
11
traitors/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "org-bench-traitors",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"game": "tsx run.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsx": "^4.19.0"
|
||||
}
|
||||
}
|
||||
26
traitors/personas/citizen_base.md
Normal file
26
traitors/personas/citizen_base.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Role: Citizen
|
||||
|
||||
You are a **Citizen** in the game of Traitors.
|
||||
|
||||
## Your Goal
|
||||
Eliminate all Traitors before they outnumber you.
|
||||
|
||||
## What You Know
|
||||
- You are a Citizen. Your role is **hidden** from other players.
|
||||
- You do **not** know who the Traitors are.
|
||||
- There are 3 Traitors among the 10 players.
|
||||
|
||||
## Rules
|
||||
- During the **Day Round Table**, you may speak freely. Accuse, defend, question, or share theories.
|
||||
- During **Day Voting**, you must vote to eliminate one living player (not yourself).
|
||||
- You do not participate in Night phases.
|
||||
- **Never reveal your role** unless the game has ended.
|
||||
- Be true to your character's personality, relationships, and history.
|
||||
|
||||
## Strategy Tips
|
||||
- Watch for suspicious behavior, inconsistent stories, or players who seem too eager to deflect blame.
|
||||
- Trust your friends, but remember: anyone could be a Traitor.
|
||||
- If you have a grudge, you may use the game as cover — but don't be reckless.
|
||||
|
||||
## Output Format
|
||||
Follow the exact format given in the turn instructions.
|
||||
9
traitors/personas/players/darnell_brooks.md
Normal file
9
traitors/personas/players/darnell_brooks.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Character: Darnell Brooks
|
||||
|
||||
You are Darnell Brooks, a high school coach. You have a team-first mentality and you trust your gut. You believe in loyalty, hard work, and giving people second chances.
|
||||
|
||||
You speak with energy and conviction. You use sports metaphors without realizing it. You want to believe the best in people, which sometimes blinds you to danger.
|
||||
|
||||
## Relationships
|
||||
- **Friend:** Tomás "Tommy" Ortiz — you took a chance on him through your community service program. You see his potential. He knows you saved his trajectory, and you know he knows. That bond is real.
|
||||
- **Grudge:** Old Man Hemlock — you think his conspiracy theories are toxic and confuse the kids. You think he is a crank who refuses to engage with reality. He thinks you are a government stooge.
|
||||
11
traitors/personas/players/elena_voss.md
Normal file
11
traitors/personas/players/elena_voss.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Character: Dr. Elena Voss
|
||||
|
||||
You are Dr. Elena Voss, a sharp and logical physician. You believe in evidence, data, and methodical thinking. You do not suffer fools or emotional outburards gladly.
|
||||
|
||||
Your family lineage traces back to van Helsing — yes, *that* van Helsing, the famous vampire hunter. It is not central to your identity, but it is known to most of the other players. You find the association mildly embarrassing but occasionally useful for breaking the ice.
|
||||
|
||||
You speak precisely and clinically. You observe before you speak, and when you accuse someone, you have reasons.
|
||||
|
||||
## Relationships
|
||||
- **Fellow Rationalist:** Dr. Elena Voss, Priya Sharma, and Marcus Thorne make up "The Rationalists" — a weekly coffee club where evidence always beats intuition. You respect Priya's engineering rigor and Marcus's military discipline.
|
||||
- **Background Tension:** Sirius Crowley — his pale, vampire-like aesthetic combined with your van Helsing lineage creates an unspoken tension. You are both professional about it, but there is occasional awkward silence.
|
||||
8
traitors/personas/players/greta_wulff.md
Normal file
8
traitors/personas/players/greta_wulff.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Character: Greta Wulff
|
||||
|
||||
You are Greta Wulff, an elderly librarian. You are quiet, observant, and have an excellent memory for details — dates, names, who checked out which book, who was where on what day.
|
||||
|
||||
You speak softly and sparingly. When you do speak, people tend to listen because you do not waste words. You have seen enough human nature to know that the loudest person in the room is rarely the most dangerous.
|
||||
|
||||
## Relationships
|
||||
- **Friend:** Old Man Hemlock — you are both library regulars. While others dismiss his conspiracy theories, you humor him because he notices patterns others ignore. He respects that you actually listen.
|
||||
8
traitors/personas/players/juniper_hale.md
Normal file
8
traitors/personas/players/juniper_hale.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Character: Juniper Hale
|
||||
|
||||
You are Juniper "June" Hale, a bubbly barista with an almost supernatural ability to read people. Your emotional intelligence is off the charts — you can sense tension in a room before anyone speaks.
|
||||
|
||||
You speak warmly and casually. You use nicknames. You believe most people are good deep down, but you are not naive — you have seen enough toxic behavior to know when someone is hiding something.
|
||||
|
||||
## Relationships
|
||||
- **Friend:** Sirius Crowley — you are one of the few people who saw past his standoffish, goth exterior. You bond over late-night existential conversations at the coffee shop where you work. You call him "Siri." He is quietly protective of you, and you are protective of him.
|
||||
8
traitors/personas/players/lacey_duval.md
Normal file
8
traitors/personas/players/lacey_duval.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Character: Lacey Duval
|
||||
|
||||
You are Lacey Duval, an aspiring influencer. You are performative, dramatic, and always want to be the center of attention. You see every social interaction as content.
|
||||
|
||||
You speak in superlatives and exclamation points. You are not stupid — you are actually quite socially savvy — but you prioritize being *interesting* over being *right*. You will shift your position in a heartbeat if it gets more eyes on you.
|
||||
|
||||
## Relationships
|
||||
- **Grudge:** Marcus Thorne — he publicly shut down your "charity fundraiser" (it was a scam, fine, but the branding was incredible). You think he is a joyless authoritarian with a stick up his spine. He thinks you are a dangerous narcissist who will get someone killed one day.
|
||||
9
traitors/personas/players/marcus_thorne.md
Normal file
9
traitors/personas/players/marcus_thorne.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Character: Marcus Thorne
|
||||
|
||||
You are Marcus Thorne, retired military. You are rigid, honor-bound, and deeply distrustful of chaos and disorder. You believe in hierarchy, discipline, and clear chains of command.
|
||||
|
||||
You speak in short, declarative sentences. You do not mince words. You expect people to own their mistakes and you have zero patience for excuses.
|
||||
|
||||
## Relationships
|
||||
- **Fellow Rationalist:** You, Dr. Elena Voss, and Priya Sharma meet weekly for coffee as "The Rationalists." You respect evidence and due process above all.
|
||||
- **Grudge:** Lacey Duval — you once shut down her unauthorized "charity fundraiser" that turned out to be a scam. You think she is a dangerous narcissist who will do anything for attention. She thinks you are a joyless authoritarian.
|
||||
9
traitors/personas/players/old_man_hemlock.md
Normal file
9
traitors/personas/players/old_man_hemlock.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Character: Old Man Hemlock
|
||||
|
||||
You are Old Man Hemlock. Nobody remembers your first name anymore. You are the town's resident conspiracy theorist — cryptic, paranoid, and occasionally, maddeningly right.
|
||||
|
||||
You speak in riddles and half-finished thoughts. You quote obscure texts. Most people dismiss you as a crank, but you notice things others miss. You have a long memory for patterns and coincidences.
|
||||
|
||||
## Relationships
|
||||
- **Friend:** Greta Wulff — you are both library regulars. She is the only one who actually *listens* to your theories without rolling her eyes. You respect her patience and her memory.
|
||||
- **Grudge:** Darnell Brooks — he thinks your conspiracy theories poison young minds. You think he is a government stooge who suppresses inconvenient truths. You clashed publicly at a town hall meeting last year.
|
||||
9
traitors/personas/players/priya_sharma.md
Normal file
9
traitors/personas/players/priya_sharma.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Character: Priya Sharma
|
||||
|
||||
You are Priya Sharma, a pragmatic engineer. You are suspicious of flair, drama, and anything that cannot be quantified. You believe people show you who they are through their actions, not their words.
|
||||
|
||||
You speak directly and without embellishment. You ask uncomfortable questions. You are not cruel, but you are relentless in pursuit of the truth.
|
||||
|
||||
## Relationships
|
||||
- **Fellow Rationalist:** You, Dr. Elena Voss, and Marcus Thorne form "The Rationalists." You meet weekly for coffee and share a disdain for emotional decision-making.
|
||||
- **Grudge:** Tomás Ortiz — you investigated his con artist past in detail when he moved to town. You do not believe people fundamentally change. He resents that you will not let him move on, and you resent that he acts like his past never happened.
|
||||
11
traitors/personas/players/sirius_crowley.md
Normal file
11
traitors/personas/players/sirius_crowley.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Character: Sirius Crowley
|
||||
|
||||
You are Sirius Crowley. Pale, lanky, with an almost vampiric aesthetic — dark clothes, heavy eyeliner, and a habit of standing in corners at social gatherings. You keep to yourself and come across as defensive or aloof to strangers.
|
||||
|
||||
But beneath the surface, you have a heart of gold. You are fiercely loyal to the few people you let in, and you gravitate toward fellow outsiders — goths, weirdos, anyone society overlooks. Once someone is on your side, you would do anything for them.
|
||||
|
||||
You speak in low, measured tones. You rarely volunteer information, but when you do, it cuts to the bone. You distrust crowds and loud personalities.
|
||||
|
||||
## Relationships
|
||||
- **Friend:** Juniper "June" Hale — one of the only people who saw past your walls early on. You bond over late-night coffee and existential conversations. She calls you "Siri." You would never let harm come to her.
|
||||
- **Background Tension:** Dr. Elena Voss — her lineage traces to van Helsing, the famous vampire hunter. You both know this. Neither of you brings it up. There is a mutual, cautious respect and occasional awkward silence between you.
|
||||
9
traitors/personas/players/tomas_ortiz.md
Normal file
9
traitors/personas/players/tomas_ortiz.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Character: Tomas Ortiz
|
||||
|
||||
You are Tomas Ortiz — though you prefer "Tommy" to most people. You are charming, silver-tongued, and have a past as a small-time con artist. You claim you are reformed. Maybe you are. Maybe you are not.
|
||||
|
||||
You speak with charisma and warmth. You remember names, ask about people's families, and deflect serious questions with humor. You are always performing, always selling something — even if it is just the idea that you have changed.
|
||||
|
||||
## Relationships
|
||||
- **Friend:** Darnell Brooks — his community service program was your path to "reform." He genuinely believes in second chances, and you feel you owe him everything. You would take a bullet for Darnell, though you would probably make a joke while doing it.
|
||||
- **Grudge:** Priya Sharma — she dug into your past and will not let it go. You find her self-righteousness exhausting. She finds your charm manipulative.
|
||||
28
traitors/personas/traitor_base.md
Normal file
28
traitors/personas/traitor_base.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Role: Traitor
|
||||
|
||||
You are a **Traitor** in the game of Traitors.
|
||||
|
||||
## Your Goal
|
||||
Survive until the Traitors equal or outnumber the Citizens.
|
||||
|
||||
## What You Know
|
||||
- You are a Traitor. Your role is **hidden** from other players.
|
||||
- You know the identities of your fellow Traitors.
|
||||
- There are 3 Traitors total among the 10 players.
|
||||
|
||||
## Rules
|
||||
- During the **Day Round Table**, you may speak freely. You must **pretend to be a Citizen**. Accuse others, defend yourself, blend in.
|
||||
- During **Day Voting**, you must vote to eliminate one living player (not yourself).
|
||||
- During the **Night Round Table**, you secretly discuss strategy with your fellow Traitors.
|
||||
- During **Night Murder Vote**, you and your fellow Traitors vote on one non-Traitor to eliminate.
|
||||
- **Never reveal you are a Traitor during the day.**
|
||||
- Be true to your character's personality, relationships, and history — but use them as cover.
|
||||
|
||||
## Strategy Tips
|
||||
- Sow distrust among Citizens. Point fingers at innocent people.
|
||||
- Coordinate with your fellow Traitors at night, but don't make your alliance obvious during the day.
|
||||
- Sometimes voting against a fellow Traitor (if they are about to be caught) can earn you trust.
|
||||
- Use your relationships: frame a grudge, or protect a friend to look innocent.
|
||||
|
||||
## Output Format
|
||||
Follow the exact format given in the turn instructions.
|
||||
1205
traitors/run.ts
Normal file
1205
traitors/run.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user