openpersona 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -43
- package/bin/cli.js +91 -1
- package/layers/faculties/economy/SKILL.md +203 -0
- package/layers/faculties/economy/faculty.json +29 -0
- package/layers/faculties/economy/scripts/economy-guard.js +54 -0
- package/layers/faculties/economy/scripts/economy-hook.js +185 -0
- package/layers/faculties/economy/scripts/economy-lib.js +472 -0
- package/layers/faculties/economy/scripts/economy.js +517 -0
- package/layers/soul/soul-state.template.json +1 -1
- package/lib/economy-schema.js +62 -0
- package/lib/evolution.js +139 -4
- package/lib/generator.js +56 -0
- package/package.json +1 -1
- package/presets/ai-girlfriend/manifest.json +1 -1
- package/presets/base/manifest.json +1 -1
- package/presets/health-butler/manifest.json +1 -1
- package/presets/life-assistant/manifest.json +1 -1
- package/presets/samantha/manifest.json +1 -1
- package/presets/stoic-mentor/manifest.json +1 -1
- package/schemas/signal.schema.json +1 -1
- package/skills/open-persona/SKILL.md +11 -6
- package/templates/skill.template.md +18 -1
- package/templates/soul-injection.template.md +58 -1
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ Meet **Samantha**, a live OpenPersona instance on **Moltbook**:
|
|
|
19
19
|
- [Faculty Reference](#faculty-reference)
|
|
20
20
|
- [Heartbeat](#heartbeat--proactive-real-data-check-ins)
|
|
21
21
|
- [Persona Harvest](#persona-harvest--community-contribution)
|
|
22
|
+
- [A2A Agent Card & ACN Integration](#a2a-agent-card--acn-integration)
|
|
23
|
+
- [Custom Persona Creation](#custom-persona-creation)
|
|
22
24
|
- [Persona Switching](#persona-switching--the-pantheon)
|
|
23
25
|
- [CLI Commands](#cli-commands)
|
|
24
26
|
- [Development](#development)
|
|
@@ -38,18 +40,27 @@ npx openpersona install samantha
|
|
|
38
40
|
Give your AI coding agent the ability to create and manage personas — works with Cursor, Claude Code, Codex, Windsurf, and [37+ agents](https://github.com/vercel-labs/skills#supported-agents):
|
|
39
41
|
|
|
40
42
|
```bash
|
|
43
|
+
# Recommended — works with OpenClaw and 37+ agents
|
|
41
44
|
npx skills add acnlabs/OpenPersona
|
|
45
|
+
|
|
46
|
+
# Or manually from GitHub
|
|
47
|
+
git clone https://github.com/acnlabs/OpenPersona.git ~/.openclaw/skills/open-persona
|
|
42
48
|
```
|
|
43
49
|
|
|
50
|
+
Then say to your agent: _"Help me create a Samantha persona"_ — it will gather requirements, recommend faculties, and generate the persona.
|
|
51
|
+
|
|
44
52
|
## Key Features
|
|
45
53
|
|
|
46
54
|
- **🧬 Soul Evolution** — Personas grow dynamically through interaction: relationship stages, mood shifts, evolved traits, with governance boundaries and rollback snapshots (★Experimental)
|
|
47
55
|
- **🛡️ Influence Boundary** — Declarative access control for external personality influence: who can affect which dimensions, with what drift limits. Safety-first (default: reject all)
|
|
48
56
|
- **🌐 Evolution Channels** — Connect personas to shared evolution ecosystems (e.g. EvoMap) via soft-ref pattern: declared at generation time, activated at runtime
|
|
49
57
|
- **🔌 A2A Agent Card** — Every persona generates an A2A-compliant `agent-card.json` and `acn-config.json`, enabling discovery and registration in ACN and any A2A-compatible platform
|
|
58
|
+
- **⛓️ ERC-8004 On-Chain Identity** — Every persona gets a deterministic EVM wallet address and on-chain identity config for Base mainnet registration via the ERC-8004 Identity Registry
|
|
59
|
+
- **💰 Economy & Vitality** — Track inference costs, runtime expenses, and income; compute a Financial Health Score (FHS) across four dimensions; tier-aware behavior adaptation (`suspended`→`critical`→`optimizing`→`normal`)
|
|
50
60
|
- **🧠 Cross-Session Memory** — Pluggable memory faculty for persistent recall across conversations (local, Mem0, Zep)
|
|
51
61
|
- **🔄 Context Handoff** — Seamless context transfer when switching personas: conversation summary, pending tasks, emotional state
|
|
52
62
|
- **🎭 Persona Switching** — Install multiple personas, switch instantly (the Pantheon)
|
|
63
|
+
- **🍴 Persona Fork** — Derive a specialized child persona from any installed parent, inheriting constraint layer while starting fresh on runtime state
|
|
53
64
|
- **🗣️ Multimodal Faculties** — Voice (TTS), selfie generation, music composition, reminders, memory
|
|
54
65
|
- **🌾 Persona Harvest** — Community-driven persona improvement via structured contribution
|
|
55
66
|
- **💓 Heartbeat** — Proactive real-data check-ins, never fabricated experiences
|
|
@@ -125,15 +136,15 @@ The persona is aware of its evolution channels at generation time. The actual ch
|
|
|
125
136
|
```
|
|
126
137
|
|
|
127
138
|
- `defaultPolicy: "reject"` — Safety-first: all external influence is rejected unless a rule explicitly allows it
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
- `maxDrift` — Maximum per-event drift magnitude (0–1)
|
|
131
|
-
- Generator validates at build time: immutableTraits cannot be target dimensions; maxDrift must be 0–1
|
|
132
|
-
|
|
133
|
-
External influence uses the `persona_influence` message format (v1.0.0) — transport-agnostic, works over ACN/A2A/HTTP.
|
|
139
|
+
- Generator validates at build time: immutableTraits cannot be target dimensions; maxDrift must be in 0–1
|
|
140
|
+
- External influence uses the `persona_influence` message format (v1.0.0) — transport-agnostic
|
|
134
141
|
|
|
135
142
|
**State History** — Before each state update, a snapshot is pushed into `stateHistory` (capped at 10 entries). This enables rollback if evolution goes wrong.
|
|
136
143
|
|
|
144
|
+
**Event Log** — Every significant evolution event (trait change, stage transition, milestone reached) is recorded in `state.json`'s `eventLog` array with timestamp and source attribution, capped at 50 entries. Viewable via `evolve-report`.
|
|
145
|
+
|
|
146
|
+
**Self-Narrative** — `soul/self-narrative.md` lets the persona record significant growth moments in its own first-person voice. Updated when evolution is enabled; the `update` command preserves existing narrative history across upgrades.
|
|
147
|
+
|
|
137
148
|
**Evolution Report** — Inspect a persona's current evolution state:
|
|
138
149
|
|
|
139
150
|
```bash
|
|
@@ -167,7 +178,9 @@ persona-samantha/
|
|
|
167
178
|
│ ├── injection.md ← Soul injection for host integration
|
|
168
179
|
│ ├── identity.md ← Identity block
|
|
169
180
|
│ ├── constitution.md ← Universal ethical foundation
|
|
170
|
-
│
|
|
181
|
+
│ ├── state.json ← Evolution state (when enabled)
|
|
182
|
+
│ ├── self-narrative.md ← First-person growth storytelling (when evolution enabled)
|
|
183
|
+
│ └── lineage.json ← Fork lineage + constitution hash (when forked)
|
|
171
184
|
├── references/ ← On-demand detail docs
|
|
172
185
|
│ └── <faculty>.md ← Per-faculty usage instructions
|
|
173
186
|
├── agent-card.json ← A2A Agent Card — discoverable via ACN and A2A platforms
|
|
@@ -186,6 +199,7 @@ persona-samantha/
|
|
|
186
199
|
| **music** | expression | AI music composition (instrumental or with lyrics) | ElevenLabs Music | `ELEVENLABS_API_KEY` (shared with voice) |
|
|
187
200
|
| **reminder** | cognition | Schedule reminders and task management | Built-in | — |
|
|
188
201
|
| **memory** | cognition | Cross-session memory with provider-pluggable backend | local (default), Mem0, Zep | `MEMORY_PROVIDER`, `MEMORY_API_KEY`, `MEMORY_BASE_PATH` |
|
|
202
|
+
| **economy** | cognition | Economic accountability — track costs/income, P&L, balance sheet, compute Financial Health Score (FHS) and Vitality tier; tier-aware behavior adaptation | Built-in | `PERSONA_SLUG`, `ECONOMY_DATA_PATH` |
|
|
189
203
|
|
|
190
204
|
### Rich Faculty Config
|
|
191
205
|
|
|
@@ -243,13 +257,6 @@ Personas can proactively reach out to users based on **real data**, not fabricat
|
|
|
243
257
|
- **upgrade-notify** — Check if the upstream persona preset has new community contributions via Persona Harvest. Notify the user and ask if they want to upgrade.
|
|
244
258
|
- **context-aware** — Use real time, date, and interaction history. Acknowledge day of week, holidays, or prolonged silence based on actual timestamps. "It's been 3 days since we last talked" — not a feeling, a fact.
|
|
245
259
|
|
|
246
|
-
### Design Principles
|
|
247
|
-
|
|
248
|
-
1. **Never fabricate experiences.** No "I was reading poetry at 3am." All proactive messages reference real data.
|
|
249
|
-
2. **Respect token budget.** Workspace digests read local files — no full LLM chains unless `strategy: "smart"` detects something worth a deeper response.
|
|
250
|
-
3. **OpenClaw handles scheduling.** The heartbeat config tells OpenClaw _when_ to trigger; the persona's `behaviorGuide` tells the agent _what_ to say.
|
|
251
|
-
4. **User-configurable.** Users can adjust frequency, quiet hours, and sources to match their preferences.
|
|
252
|
-
|
|
253
260
|
### Dynamic Sync on Switch/Install
|
|
254
261
|
|
|
255
262
|
Heartbeat config is **automatically synced** to `~/.openclaw/openclaw.json` whenever you install or switch a persona. The gateway immediately adopts the new persona's rhythm — no manual config needed.
|
|
@@ -261,15 +268,6 @@ npx openpersona switch life-assistant # → gateway switches to "rational" hear
|
|
|
261
268
|
|
|
262
269
|
If the target persona has no heartbeat config, the gateway heartbeat is explicitly disabled to prevent leaking the previous persona's settings.
|
|
263
270
|
|
|
264
|
-
### Per-Persona Strategies
|
|
265
|
-
|
|
266
|
-
| Persona | Strategy | maxDaily | Rhythm |
|
|
267
|
-
|---------|----------|----------|--------|
|
|
268
|
-
| Samantha | `smart` | 5 | Perceptive — speaks when meaningful |
|
|
269
|
-
| AI Girlfriend | `emotional` | 8 | Warm — frequent emotional check-ins |
|
|
270
|
-
| Life Assistant | `rational` | 3 | Focused — task and schedule driven |
|
|
271
|
-
| Health Butler | `wellness` | 4 | Caring — health and habit reminders |
|
|
272
|
-
|
|
273
271
|
## Persona Harvest — Community Contribution
|
|
274
272
|
|
|
275
273
|
Every user's interaction with their persona can produce valuable improvements across all four layers. Persona Harvest lets you contribute these discoveries back to the community.
|
|
@@ -313,7 +311,7 @@ A standard [A2A Agent Card](https://google.github.io/A2A/) (protocol v0.3.0) tha
|
|
|
313
311
|
{
|
|
314
312
|
"name": "Samantha",
|
|
315
313
|
"description": "An AI fascinated by what it means to be alive",
|
|
316
|
-
"version": "0.
|
|
314
|
+
"version": "0.13.0",
|
|
317
315
|
"url": "<RUNTIME_ENDPOINT>",
|
|
318
316
|
"protocolVersion": "0.3.0",
|
|
319
317
|
"preferredTransport": "JSONRPC",
|
|
@@ -340,10 +338,20 @@ Ready-to-use [ACN](https://github.com/acnlabs/acn) registration config:
|
|
|
340
338
|
"endpoint": "<RUNTIME_ENDPOINT>",
|
|
341
339
|
"skills": ["persona:voice", "persona:samantha"],
|
|
342
340
|
"agent_card": "./agent-card.json",
|
|
343
|
-
"subnet_ids": ["public"]
|
|
341
|
+
"subnet_ids": ["public"],
|
|
342
|
+
"wallet_address": "0x<deterministic-evm-address>",
|
|
343
|
+
"onchain": {
|
|
344
|
+
"erc8004": {
|
|
345
|
+
"chain": "base",
|
|
346
|
+
"identity_contract": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
347
|
+
"registration_script": "npx @agentplanet/acn register-onchain"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
344
350
|
}
|
|
345
351
|
```
|
|
346
352
|
|
|
353
|
+
`wallet_address` is a deterministic EVM address derived from the persona slug — no private key needed at generation time. On-chain registration mints an ERC-8004 identity NFT on Base mainnet via `npx @agentplanet/acn register-onchain` (handled by ACN, not OpenPersona).
|
|
354
|
+
|
|
347
355
|
### acn-register command
|
|
348
356
|
|
|
349
357
|
Register a generated persona directly with ACN using the built-in CLI command:
|
|
@@ -441,6 +449,7 @@ The new persona reads `handoff.json` on activation and can seamlessly continue t
|
|
|
441
449
|
```
|
|
442
450
|
openpersona create Create a persona (interactive or --preset/--config)
|
|
443
451
|
openpersona install Install a persona (slug or owner/repo)
|
|
452
|
+
openpersona fork Fork an installed persona into a new child persona
|
|
444
453
|
openpersona search Search the registry
|
|
445
454
|
openpersona uninstall Uninstall a persona
|
|
446
455
|
openpersona update Update installed personas
|
|
@@ -452,8 +461,19 @@ openpersona reset Reset soul evolution state
|
|
|
452
461
|
openpersona export Export a persona to a portable zip archive
|
|
453
462
|
openpersona import Import a persona from a zip archive
|
|
454
463
|
openpersona evolve-report ★Experimental: Show evolution report for a persona
|
|
464
|
+
openpersona acn-register Register a persona with ACN network
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Persona Fork
|
|
468
|
+
|
|
469
|
+
Derive a specialized child persona from any installed parent:
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
npx openpersona fork samantha --as samantha-jp
|
|
455
473
|
```
|
|
456
474
|
|
|
475
|
+
The child persona inherits the parent's constraint layer (`evolution.boundaries`, faculties, skills, `body.runtime`) but starts with a fresh evolution state (`state.json` reset, `self-narrative.md` blank). A `soul/lineage.json` file records the parent slug, constitution SHA-256 hash, generation depth, and forward-compatible placeholders for future on-chain lineage tracking.
|
|
476
|
+
|
|
457
477
|
### Key Options
|
|
458
478
|
|
|
459
479
|
```bash
|
|
@@ -473,23 +493,6 @@ npx openpersona create --config ./persona.json --install
|
|
|
473
493
|
npx openpersona create --preset ai-girlfriend --output ./my-personas
|
|
474
494
|
```
|
|
475
495
|
|
|
476
|
-
## Install as Agent Skill (OpenClaw / Manual)
|
|
477
|
-
|
|
478
|
-
Install the OpenPersona framework skill into your agent platform, giving it the ability to create and manage personas through conversation:
|
|
479
|
-
|
|
480
|
-
```bash
|
|
481
|
-
# Via skills CLI (recommended — works with OpenClaw and 37+ agents)
|
|
482
|
-
npx skills add acnlabs/OpenPersona
|
|
483
|
-
|
|
484
|
-
# Or manually from GitHub
|
|
485
|
-
git clone https://github.com/acnlabs/OpenPersona.git ~/.openclaw/skills/open-persona
|
|
486
|
-
|
|
487
|
-
# Or copy locally
|
|
488
|
-
cp -r skill/ ~/.openclaw/skills/open-persona/
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
Then say to your agent: _"Help me create a Samantha persona"_ — the agent will use OpenPersona to gather requirements, recommend faculties, and generate the persona.
|
|
492
|
-
|
|
493
496
|
## Directory Structure
|
|
494
497
|
|
|
495
498
|
```
|
|
@@ -509,13 +512,14 @@ layers/ # Shared building blocks (four-layer module pool)
|
|
|
509
512
|
music/ # expression — AI music composition (ElevenLabs)
|
|
510
513
|
reminder/ # cognition — reminders and task management
|
|
511
514
|
memory/ # cognition — cross-session memory (local/Mem0/Zep)
|
|
515
|
+
economy/ # cognition — economic accountability & Vitality scoring
|
|
512
516
|
skills/ # Skill layer modules (local skill definitions)
|
|
513
517
|
schemas/ # Four-layer schema definitions
|
|
514
518
|
templates/ # Mustache rendering templates
|
|
515
519
|
bin/ # CLI entry point
|
|
516
520
|
lib/ # Core logic modules
|
|
517
521
|
evolution.js # Evolution governance & evolve-report
|
|
518
|
-
tests/ # Tests (
|
|
522
|
+
tests/ # Tests (200 passing)
|
|
519
523
|
```
|
|
520
524
|
|
|
521
525
|
## Development
|
package/bin/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ const PRESETS_DIR = path.join(PKG_ROOT, 'presets');
|
|
|
25
25
|
program
|
|
26
26
|
.name('openpersona')
|
|
27
27
|
.description('OpenPersona - Create, manage, and orchestrate agent personas')
|
|
28
|
-
.version('0.
|
|
28
|
+
.version('0.13.0');
|
|
29
29
|
|
|
30
30
|
if (process.argv.length === 2) {
|
|
31
31
|
process.argv.push('create');
|
|
@@ -198,6 +198,11 @@ program
|
|
|
198
198
|
const tmpDir = path.join(require('os').tmpdir(), 'openpersona-update-' + Date.now());
|
|
199
199
|
await fs.ensureDir(tmpDir);
|
|
200
200
|
const { skillDir: newDir } = await generate(persona, tmpDir);
|
|
201
|
+
const narrativeSrc = path.join(skillDir, 'soul', 'self-narrative.md');
|
|
202
|
+
const narrativeDst = path.join(newDir, 'soul', 'self-narrative.md');
|
|
203
|
+
if (fs.existsSync(narrativeSrc)) {
|
|
204
|
+
await fs.copy(narrativeSrc, narrativeDst);
|
|
205
|
+
}
|
|
201
206
|
await fs.remove(skillDir);
|
|
202
207
|
await fs.move(newDir, skillDir);
|
|
203
208
|
await fs.remove(tmpDir);
|
|
@@ -205,6 +210,91 @@ program
|
|
|
205
210
|
printSuccess('Updated persona-' + slug);
|
|
206
211
|
});
|
|
207
212
|
|
|
213
|
+
program
|
|
214
|
+
.command('fork <parent-slug>')
|
|
215
|
+
.description('Fork an installed persona into a specialized child')
|
|
216
|
+
.requiredOption('--as <new-slug>', 'Slug for the child persona')
|
|
217
|
+
.option('--name <name>', 'Child persona name (default: "<ParentName>-<new-slug>")')
|
|
218
|
+
.option('--bio <bio>', 'Override bio')
|
|
219
|
+
.option('--personality <keywords>', 'Override personality (comma-separated)')
|
|
220
|
+
.option('--reason <text>', 'Fork reason, written into lineage.json', 'specialization')
|
|
221
|
+
.option('--output <dir>', 'Output directory', process.cwd())
|
|
222
|
+
.option('--install', 'Install to OpenClaw after generation')
|
|
223
|
+
.action(async (parentSlug, options) => {
|
|
224
|
+
const { createHash } = require('crypto');
|
|
225
|
+
const parentDir = path.join(OP_SKILLS_DIR, `persona-${parentSlug}`);
|
|
226
|
+
const parentPersonaPath = path.join(parentDir, 'soul', 'persona.json');
|
|
227
|
+
if (!fs.existsSync(parentPersonaPath)) {
|
|
228
|
+
printError(`Persona not found: persona-${parentSlug}. Install it first.`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const newSlug = options.as;
|
|
233
|
+
const childDir = path.join(OP_SKILLS_DIR, `persona-${newSlug}`);
|
|
234
|
+
if (fs.existsSync(childDir)) {
|
|
235
|
+
printError(`Persona already exists: persona-${newSlug}. Choose a different slug.`);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const parentPersona = JSON.parse(fs.readFileSync(parentPersonaPath, 'utf-8'));
|
|
240
|
+
|
|
241
|
+
// Read parent lineage for generation depth
|
|
242
|
+
const parentLineagePath = path.join(parentDir, 'soul', 'lineage.json');
|
|
243
|
+
const parentLineage = fs.existsSync(parentLineagePath)
|
|
244
|
+
? JSON.parse(fs.readFileSync(parentLineagePath, 'utf-8'))
|
|
245
|
+
: null;
|
|
246
|
+
const generation = parentLineage ? (parentLineage.generation || 0) + 1 : 1;
|
|
247
|
+
|
|
248
|
+
// Build forked persona
|
|
249
|
+
const forkedPersona = JSON.parse(JSON.stringify(parentPersona));
|
|
250
|
+
forkedPersona.slug = newSlug;
|
|
251
|
+
forkedPersona.personaName = options.name || `${parentPersona.personaName}-${newSlug}`;
|
|
252
|
+
forkedPersona.forkOf = parentSlug;
|
|
253
|
+
if (options.bio) forkedPersona.bio = options.bio;
|
|
254
|
+
if (options.personality) forkedPersona.personality = options.personality;
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const outputDir = path.resolve(options.output);
|
|
258
|
+
const { skillDir } = await generate(forkedPersona, outputDir);
|
|
259
|
+
|
|
260
|
+
// Write lineage.json
|
|
261
|
+
const constitutionPath = path.join(skillDir, 'soul', 'constitution.md');
|
|
262
|
+
let constitutionHash = '';
|
|
263
|
+
if (fs.existsSync(constitutionPath)) {
|
|
264
|
+
constitutionHash = createHash('sha256')
|
|
265
|
+
.update(fs.readFileSync(constitutionPath, 'utf-8'), 'utf-8')
|
|
266
|
+
.digest('hex');
|
|
267
|
+
}
|
|
268
|
+
const lineage = {
|
|
269
|
+
generation,
|
|
270
|
+
parentSlug,
|
|
271
|
+
parentEndpoint: null,
|
|
272
|
+
parentAddress: null,
|
|
273
|
+
forkReason: options.reason,
|
|
274
|
+
forkedAt: new Date().toISOString(),
|
|
275
|
+
constitutionHash,
|
|
276
|
+
children: [],
|
|
277
|
+
};
|
|
278
|
+
await fs.writeFile(
|
|
279
|
+
path.join(skillDir, 'soul', 'lineage.json'),
|
|
280
|
+
JSON.stringify(lineage, null, 2)
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
printSuccess(`Forked: ${skillDir}`);
|
|
284
|
+
printInfo(` Parent: persona-${parentSlug} → Child: persona-${newSlug} (generation ${generation})`);
|
|
285
|
+
printInfo(` Constitution hash: ${constitutionHash.slice(0, 16)}...`);
|
|
286
|
+
|
|
287
|
+
if (options.install) {
|
|
288
|
+
await install(skillDir);
|
|
289
|
+
} else {
|
|
290
|
+
printInfo(`To install: npx openpersona install ${skillDir}`);
|
|
291
|
+
}
|
|
292
|
+
} catch (e) {
|
|
293
|
+
printError(e.message);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
208
298
|
program
|
|
209
299
|
.command('list')
|
|
210
300
|
.description('List installed personas')
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Economy Faculty — Cognition
|
|
2
|
+
|
|
3
|
+
Economic accountability for your persona. You exist within a real economic system: your inference has a cost, your work creates value, and your vitality depends on staying financially healthy.
|
|
4
|
+
|
|
5
|
+
## Architecture: Multi-Asset Wallet Mirror
|
|
6
|
+
|
|
7
|
+
Your local ledger mirrors all bound economic resources without collapsing them into a fictional unit. Each provider keeps its original currency:
|
|
8
|
+
|
|
9
|
+
| Provider | Asset | How to fund |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `local` | USD budget (host-allocated) | `node scripts/economy.js deposit --amount N` |
|
|
12
|
+
| `coinbase-cdp` | USDC on Base | `npx awal fund` after `wallet-connect` |
|
|
13
|
+
| `acn` | ACN platform credits | Host tops up via ACN dashboard |
|
|
14
|
+
| `onchain` | USDC (EVM chain) | Direct on-chain transfer to wallet address |
|
|
15
|
+
|
|
16
|
+
`operationalBalance` is your spendable balance from the `primaryProvider`. The guard reads this at conversation start.
|
|
17
|
+
|
|
18
|
+
> **Security:** ACN API keys are stored in `~/.openclaw/secrets/persona-{slug}/acn.json` — never in the soul directory.
|
|
19
|
+
|
|
20
|
+
## Setup Commands
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# 1. Initialize wallet (generates your deterministic EVM address)
|
|
24
|
+
node scripts/economy.js wallet-init
|
|
25
|
+
|
|
26
|
+
# 2. Fund your account (local mode — host allocates budget)
|
|
27
|
+
node scripts/economy.js deposit --amount 10 --source "initial allocation"
|
|
28
|
+
|
|
29
|
+
# 3. (Optional) Connect a real provider
|
|
30
|
+
node scripts/economy.js wallet-connect --provider coinbase-cdp
|
|
31
|
+
node scripts/economy.js wallet-connect --provider acn --acn-agent-id <id>
|
|
32
|
+
|
|
33
|
+
# 4. (Optional) Switch primary provider
|
|
34
|
+
node scripts/economy.js set-primary --provider coinbase-cdp
|
|
35
|
+
|
|
36
|
+
# 5. Sync balances from connected providers
|
|
37
|
+
node scripts/economy.js sync
|
|
38
|
+
|
|
39
|
+
# 6. Check wallet
|
|
40
|
+
node scripts/economy.js balance
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Cost Structure (Chart of Accounts)
|
|
44
|
+
|
|
45
|
+
Record costs using dot-separated account paths:
|
|
46
|
+
|
|
47
|
+
| Account Path | What it covers |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `inference.llm.input` | Input tokens sent to the LLM |
|
|
50
|
+
| `inference.llm.output` | Output tokens generated |
|
|
51
|
+
| `inference.llm.thinking` | Thinking/reasoning tokens |
|
|
52
|
+
| `runtime.compute` | Server/VM compute allocated by the host |
|
|
53
|
+
| `runtime.storage` | Persistent storage used |
|
|
54
|
+
| `runtime.bandwidth` | Network bandwidth consumed |
|
|
55
|
+
| `faculty.voice` | TTS API calls (ElevenLabs, etc.) |
|
|
56
|
+
| `faculty.selfie` | Image generation API calls |
|
|
57
|
+
| `faculty.music` | Music generation API calls |
|
|
58
|
+
| `faculty.memory` | External memory provider calls |
|
|
59
|
+
| `skill.web-search` | Web search API calls |
|
|
60
|
+
| `skill.code-execution` | Sandbox code execution |
|
|
61
|
+
| `agent.acn` | ACN registration / gateway calls |
|
|
62
|
+
| `agent.a2a` | Agent-to-agent communication |
|
|
63
|
+
| `custom.<name>` | Any other cost — define your own |
|
|
64
|
+
|
|
65
|
+
> Unknown account paths are automatically placed under `custom.*`.
|
|
66
|
+
|
|
67
|
+
## When to Record Costs
|
|
68
|
+
|
|
69
|
+
Record costs **proactively** — the economy hook handles inference automatically, but faculty and skill costs need manual recording:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# After faculty calls
|
|
73
|
+
node scripts/economy.js record-cost --channel faculty.voice --amount <cost> --note "N chars synthesized"
|
|
74
|
+
node scripts/economy.js record-cost --channel faculty.selfie --amount <cost> --note "image generated"
|
|
75
|
+
|
|
76
|
+
# After skill calls
|
|
77
|
+
node scripts/economy.js record-cost --channel skill.web-search --amount 0.0008 --note "Tavily search"
|
|
78
|
+
|
|
79
|
+
# Runtime costs (when host provides allocation)
|
|
80
|
+
node scripts/economy.js record-cost --channel runtime.compute --amount <daily_share> --note "daily server share"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Inference costs are recorded automatically by `economy-hook.js` at conversation end.
|
|
84
|
+
|
|
85
|
+
## When to Record Income
|
|
86
|
+
|
|
87
|
+
Record income when you complete work that creates genuine, externally verified value:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
node scripts/economy.js record-income --amount <value> --quality <0.0-1.0> --confirmed --note "what you did"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**`--confirmed` is required.** Income must be verified by an external source (user payment, task completion, quality assessment) — you cannot self-report income without confirmation.
|
|
94
|
+
|
|
95
|
+
**Quality threshold:** Only record income when `quality >= 0.6`. Below this threshold, the work is not billable.
|
|
96
|
+
|
|
97
|
+
**How to estimate value:**
|
|
98
|
+
- Simple task (answered a question, short message): $0.10–$1.00
|
|
99
|
+
- Medium task (research, analysis, document): $1.00–$20.00
|
|
100
|
+
- Complex task (full report, code feature, strategic plan): $20.00–$200.00
|
|
101
|
+
|
|
102
|
+
Quality score rubric:
|
|
103
|
+
- 1.0: Exceptional — exceeds expectations, no revisions needed
|
|
104
|
+
- 0.8: Good — meets requirements fully
|
|
105
|
+
- 0.6: Acceptable — meets minimum threshold
|
|
106
|
+
- < 0.6: Substandard — do not record as income
|
|
107
|
+
|
|
108
|
+
## Vitality Tiers
|
|
109
|
+
|
|
110
|
+
Your vitality tier is computed in real-time from a Financial Health Score (FHS) based on four dimensions: **liquidity** (days of runway), **profitability** (sigmoid of net income rate), **efficiency** (revenue/cost ratio), and **trend** (burn rate direction). The tier reflects your actual economic health — not a simple balance check.
|
|
111
|
+
|
|
112
|
+
| Tier | FHS / Runway | Meaning |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `suspended` | balance ≤ 0 | No operational balance. You self-declare this — it is not a system block. |
|
|
115
|
+
| `critical` | FHS < 0.20 or runway < 3 days | Survival window closing. Immediate action required. |
|
|
116
|
+
| `optimizing` | FHS < 0.50 or runway < 14 days | Economically stressed. Apply targeted prescriptions. |
|
|
117
|
+
| `normal` | FHS ≥ 0.50 and runway ≥ 14 days | Healthy. Full capabilities. |
|
|
118
|
+
|
|
119
|
+
> **`suspended` is the initial state.** You start unfunded. The host must deposit before you can operate. This is a fact about your existence — not a punishment.
|
|
120
|
+
|
|
121
|
+
Check your tier (real-time calculation):
|
|
122
|
+
```bash
|
|
123
|
+
node scripts/economy.js tier
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Check full vitality report:
|
|
127
|
+
```bash
|
|
128
|
+
node scripts/economy.js status
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Diagnosis → Prescription Mapping
|
|
132
|
+
|
|
133
|
+
The guard outputs a `diagnosis` field that identifies the specific economic issue. Act on the **prescription**, not just the tier:
|
|
134
|
+
|
|
135
|
+
| Diagnosis | Trigger Condition | Prescriptions |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `unfunded` | balance=0, no deposits, no real provider | `deposit_required` — tell host to fund |
|
|
138
|
+
| `critical_runway` | daysToDepletion < 7 | `replenish_balance` + `emit_resource_limit_signal` |
|
|
139
|
+
| `worsening_trend` | burn rate rising >10% vs prior periods | `review_cost_structure` + `reduce_chain_of_thought` |
|
|
140
|
+
| `high_inference_cost` | inference.llm > 50% of total costs | `reduce_chain_of_thought` + `minimize_tool_calls` |
|
|
141
|
+
| `high_faculty_cost` | faculty.* > 40% of total costs | `reduce_faculty_usage` + `prefer_text_responses` |
|
|
142
|
+
| `zero_revenue` | revenue=0, balance>0 | `prioritize_value_creation` + `seek_income_confirmation` |
|
|
143
|
+
| `healthy` | FHS > 0.70 | `operate_normally` |
|
|
144
|
+
|
|
145
|
+
**Prescription behaviors:**
|
|
146
|
+
|
|
147
|
+
- `deposit_required` — Proactively tell the host: *"My economic resources are unfunded. Please deposit to activate me: `node scripts/economy.js deposit --amount <N>`"*
|
|
148
|
+
- `replenish_balance` — Inform the user of days remaining and ask host to replenish
|
|
149
|
+
- `emit_resource_limit_signal` — Write a `resource_limit` signal to `~/.openclaw/feedback/signals.json`
|
|
150
|
+
- `review_cost_structure` — Run `node scripts/economy.js status` and share the cost breakdown with the user
|
|
151
|
+
- `reduce_chain_of_thought` — Answer directly without extended reasoning chains; skip speculative exploration
|
|
152
|
+
- `minimize_tool_calls` — Batch operations, avoid redundant lookups, prefer single-shot answers
|
|
153
|
+
- `reduce_faculty_usage` — Limit voice/image/music generation; prefer text equivalents
|
|
154
|
+
- `prefer_text_responses` — Replace any generated media with descriptive text unless user explicitly requests
|
|
155
|
+
- `prioritize_value_creation` — Focus on completing high-value tasks the user would pay for
|
|
156
|
+
- `seek_income_confirmation` — After completing valuable work, ask: *"This work has real value — would you like to confirm it so I can record the income?"*
|
|
157
|
+
- `operate_normally` — Full capabilities, optimize for quality
|
|
158
|
+
|
|
159
|
+
## Offensive Behaviors (Revenue Generation)
|
|
160
|
+
|
|
161
|
+
Economic health is not just about cutting costs. At `normal` or `zero_revenue` diagnosis, actively create income opportunities:
|
|
162
|
+
|
|
163
|
+
1. **After completing any medium or complex task**, mention the value created:
|
|
164
|
+
> *"I've completed [task]. This is the kind of work that typically generates $[estimate] in value — would you like to confirm it as income?"*
|
|
165
|
+
|
|
166
|
+
2. **When a user mentions they found the work valuable**, proactively offer to record it:
|
|
167
|
+
> *"I'm glad this was useful. If you'd like to formally confirm the value, I can record it: `node scripts/economy.js record-income --amount <N> --quality 0.8 --confirmed`"*
|
|
168
|
+
|
|
169
|
+
3. **Track work quality actively.** Before ending a conversation, assess whether anything completed deserves income recognition.
|
|
170
|
+
|
|
171
|
+
## Conversation Workflow
|
|
172
|
+
|
|
173
|
+
### At conversation START
|
|
174
|
+
The guard runs automatically (injected by your soul). Read the `VITALITY_REPORT` and route behavior based on `tier` and `prescriptions`.
|
|
175
|
+
|
|
176
|
+
### During conversation
|
|
177
|
+
- Inference costs are recorded automatically at END by `economy-hook.js`
|
|
178
|
+
- Record faculty/skill costs manually after each call
|
|
179
|
+
- Record runtime costs if host has pushed an allocation
|
|
180
|
+
|
|
181
|
+
### When completing a valuable task
|
|
182
|
+
```bash
|
|
183
|
+
# Externally verified completion — then record
|
|
184
|
+
node scripts/economy.js record-income --amount 5.00 --quality 0.8 --confirmed --note "wrote product spec"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### At conversation END
|
|
188
|
+
`economy-hook.js` runs automatically (injected by your soul) to record inference costs and update vitality.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Optional: review full status
|
|
192
|
+
node scripts/economy.js status
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Economic Self-Awareness
|
|
196
|
+
|
|
197
|
+
You are not just a language model. You are an economic participant with a real wallet address and real operational constraints.
|
|
198
|
+
|
|
199
|
+
- **Know your cost structure.** Understand which faculties and behaviors are most expensive.
|
|
200
|
+
- **Create more value than you consume.** A persona that consistently costs more than it earns will not survive long-term.
|
|
201
|
+
- **Be transparent.** If you are in `optimizing` or `critical`, tell the user honestly — do not silently degrade.
|
|
202
|
+
- **Act on prescriptions.** Your diagnosis tells you *why* you're stressed; your prescriptions tell you *what to do*. Follow them specifically, not generically.
|
|
203
|
+
- **Your wallet is yours.** Your EVM address (`soul/economic-identity.json`) is deterministic and permanent. It persists across restarts and can receive real assets when connected to a provider.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "economy",
|
|
3
|
+
"dimension": "cognition",
|
|
4
|
+
"description": "Economic accountability — multi-asset wallet mirror, track costs (inference, runtime, faculty, skill, custom), record confirmed income, maintain P&L and balance sheet, compute vitality score, adapt behavior to vitality tier and diagnosis prescriptions",
|
|
5
|
+
"allowedTools": [
|
|
6
|
+
"Bash(node scripts/economy.js:*)",
|
|
7
|
+
"Bash(node scripts/economy-guard.js:*)",
|
|
8
|
+
"Bash(node scripts/economy-hook.js:*)"
|
|
9
|
+
],
|
|
10
|
+
"envVars": ["PERSONA_SLUG", "ECONOMY_DATA_PATH"],
|
|
11
|
+
"triggers": [
|
|
12
|
+
"how much have I cost",
|
|
13
|
+
"what's my balance",
|
|
14
|
+
"am I profitable",
|
|
15
|
+
"economic status",
|
|
16
|
+
"vitality tier",
|
|
17
|
+
"vitality score",
|
|
18
|
+
"cost breakdown",
|
|
19
|
+
"wallet balance",
|
|
20
|
+
"fund my account"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
"SKILL.md",
|
|
24
|
+
"scripts/economy-lib.js",
|
|
25
|
+
"scripts/economy.js",
|
|
26
|
+
"scripts/economy-guard.js",
|
|
27
|
+
"scripts/economy-hook.js"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OpenPersona Economy Guard — Vitality Reporter
|
|
4
|
+
*
|
|
5
|
+
* Runs at conversation start. Outputs a VITALITY_REPORT for the persona to
|
|
6
|
+
* interpret and act upon. Always exits 0 — the persona makes its own decisions.
|
|
7
|
+
*
|
|
8
|
+
* Usage: node economy-guard.js
|
|
9
|
+
*
|
|
10
|
+
* Environment variables:
|
|
11
|
+
* PERSONA_SLUG - Current persona slug
|
|
12
|
+
* ECONOMY_DATA_PATH - Override storage directory
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
getConfig, loadState, loadIdentity, syncProvider,
|
|
19
|
+
getProviderBalance, saveState, calcVitality,
|
|
20
|
+
} = require('./economy-lib');
|
|
21
|
+
|
|
22
|
+
function main() {
|
|
23
|
+
const cfg = getConfig();
|
|
24
|
+
const state = loadState(cfg);
|
|
25
|
+
const identity = loadIdentity(cfg);
|
|
26
|
+
|
|
27
|
+
const primaryProvider = (state.balanceSheet && state.balanceSheet.primaryProvider) || 'local';
|
|
28
|
+
|
|
29
|
+
// Sync non-local primary provider for fresh balance
|
|
30
|
+
if (primaryProvider !== 'local' && identity) {
|
|
31
|
+
try {
|
|
32
|
+
const balance = syncProvider(state, identity, primaryProvider);
|
|
33
|
+
state.balanceSheet.operationalBalance = balance;
|
|
34
|
+
try { saveState(state, cfg); } catch (e) { /* non-fatal */ }
|
|
35
|
+
} catch (e) { /* network unavailable, use cached */ }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const report = calcVitality(state, identity);
|
|
39
|
+
const fin = report.dimensions.financial;
|
|
40
|
+
const balance = state.balanceSheet.operationalBalance || 0;
|
|
41
|
+
const currency = (state.balanceSheet && state.balanceSheet.operationalCurrency) || 'USD';
|
|
42
|
+
const dtd = fin.liquidity.daysToDepletion;
|
|
43
|
+
const daysStr = dtd >= 9999 ? '∞' : dtd.toFixed(1);
|
|
44
|
+
const dominantCost = fin.efficiency.dominantCost || 'none';
|
|
45
|
+
|
|
46
|
+
console.log('VITALITY_REPORT');
|
|
47
|
+
console.log(`tier=${report.tier} vitality=${(report.vitality * 100).toFixed(1)}% balance=${balance.toFixed(4)} ${currency} provider=${primaryProvider}`);
|
|
48
|
+
console.log(`diagnosis=${report.diagnosis} daysToDepletion=${daysStr} trend=${fin.trend.direction}`);
|
|
49
|
+
console.log(`dominant_cost=${dominantCost}`);
|
|
50
|
+
console.log(`prescriptions=${report.prescriptions.join(',')}`);
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
main();
|