openpersona 0.9.0 β 0.12.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 +180 -21
- package/bin/cli.js +60 -2
- package/layers/embodiments/README.md +7 -7
- package/layers/faculties/memory/SKILL.md +160 -0
- package/layers/faculties/memory/faculty.json +9 -0
- package/layers/faculties/memory/scripts/memory.js +315 -0
- package/layers/soul/soul-state.template.json +1 -1
- package/lib/evolution.js +142 -0
- package/lib/generator.js +235 -9
- package/lib/installer.js +1 -1
- package/lib/registrar.js +155 -0
- package/lib/switcher.js +102 -3
- package/lib/utils.js +0 -2
- package/package.json +1 -1
- package/presets/ai-girlfriend/manifest.json +1 -1
- package/presets/ai-girlfriend/persona.json +6 -0
- package/presets/base/manifest.json +1 -1
- package/presets/base/persona.json +6 -0
- package/presets/health-butler/manifest.json +1 -1
- package/presets/health-butler/persona.json +6 -0
- package/presets/life-assistant/manifest.json +1 -1
- package/presets/life-assistant/persona.json +6 -0
- package/presets/samantha/manifest.json +1 -1
- package/presets/samantha/persona.json +6 -0
- package/presets/stoic-mentor/manifest.json +1 -1
- package/presets/stoic-mentor/persona.json +6 -0
- package/schemas/acn/acn-register.schema.json +43 -0
- package/schemas/acn/agent-card.schema.json +99 -0
- package/schemas/body/embodiment.schema.json +2 -2
- package/schemas/evolution/influence-request.schema.json +79 -0
- package/schemas/manifest.schema.json +1 -1
- package/schemas/signal.schema.json +51 -0
- package/schemas/soul/evolution-event.schema.json +29 -0
- package/schemas/soul/handoff.schema.json +63 -0
- package/schemas/soul/persona.schema.json +127 -1
- package/skills/open-persona/SKILL.md +81 -18
- package/templates/handoff.template.md +30 -0
- package/templates/skill.template.md +39 -1
- package/templates/soul-injection.template.md +107 -34
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# OpenPersona π¦
|
|
2
2
|
|
|
3
|
-
The open framework for creating and orchestrating dynamic agent personas.
|
|
3
|
+
The open, agent-agnostic framework for creating and orchestrating dynamic agent personas.
|
|
4
4
|
|
|
5
|
-
Four-layer architecture β **Soul / Body / Faculty / Skill** β
|
|
5
|
+
Four-layer architecture β **Soul / Body / Faculty / Skill** β generates standard SKILL.md skill packs that work with any compatible agent. Default integration with [OpenClaw](https://github.com/openclaw/openclaw). Inspired by [Clawra](https://github.com/SumeLabs/clawra).
|
|
6
6
|
|
|
7
7
|
## π Live Demo
|
|
8
8
|
|
|
@@ -33,11 +33,24 @@ npx openpersona create --preset base --install
|
|
|
33
33
|
npx openpersona install samantha
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### Install as Agent Skill
|
|
37
|
+
|
|
38
|
+
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
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx skills add acnlabs/OpenPersona
|
|
42
|
+
```
|
|
43
|
+
|
|
36
44
|
## Key Features
|
|
37
45
|
|
|
38
|
-
- **𧬠Soul Evolution** β Personas grow dynamically through interaction: relationship stages, mood shifts, evolved traits (β
Experimental)
|
|
46
|
+
- **𧬠Soul Evolution** β Personas grow dynamically through interaction: relationship stages, mood shifts, evolved traits, with governance boundaries and rollback snapshots (β
Experimental)
|
|
47
|
+
- **π‘οΈ Influence Boundary** β Declarative access control for external personality influence: who can affect which dimensions, with what drift limits. Safety-first (default: reject all)
|
|
48
|
+
- **π Evolution Channels** β Connect personas to shared evolution ecosystems (e.g. EvoMap) via soft-ref pattern: declared at generation time, activated at runtime
|
|
49
|
+
- **π 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
|
|
50
|
+
- **π§ Cross-Session Memory** β Pluggable memory faculty for persistent recall across conversations (local, Mem0, Zep)
|
|
51
|
+
- **π Context Handoff** β Seamless context transfer when switching personas: conversation summary, pending tasks, emotional state
|
|
39
52
|
- **π Persona Switching** β Install multiple personas, switch instantly (the Pantheon)
|
|
40
|
-
- **π£οΈ Multimodal Faculties** β Voice (TTS), selfie generation, music composition, reminders
|
|
53
|
+
- **π£οΈ Multimodal Faculties** β Voice (TTS), selfie generation, music composition, reminders, memory
|
|
41
54
|
- **πΎ Persona Harvest** β Community-driven persona improvement via structured contribution
|
|
42
55
|
- **π Heartbeat** β Proactive real-data check-ins, never fabricated experiences
|
|
43
56
|
- **π¦ One-Command Install** β `npx openpersona install samantha` and you're live
|
|
@@ -57,7 +70,7 @@ flowchart TB
|
|
|
57
70
|
end
|
|
58
71
|
subgraph Faculty ["Faculty Layer"]
|
|
59
72
|
D["expression: selfie Β· voice Β· music"]
|
|
60
|
-
E["cognition: reminder"]
|
|
73
|
+
E["cognition: reminder Β· memory"]
|
|
61
74
|
end
|
|
62
75
|
subgraph Skill ["Skill Layer"]
|
|
63
76
|
F["Local definitions + ClawHub / skills.sh"]
|
|
@@ -65,7 +78,7 @@ flowchart TB
|
|
|
65
78
|
```
|
|
66
79
|
|
|
67
80
|
- **Soul** β Persona definition (constitution.md + persona.json + state.json) β all in `soul/` directory
|
|
68
|
-
- **Body** β
|
|
81
|
+
- **Body** β Substrate of existence β three dimensions: `physical` (robots/IoT), `runtime` (REQUIRED β platform/channels/credentials/resources), `appearance` (avatar/3D model). Body is never null; digital agents have a virtual body (runtime-only).
|
|
69
82
|
- **Faculty** β General software capabilities organized by dimension: Expression, Sense, Cognition
|
|
70
83
|
- **Skill** β Professional skills: local definitions in `layers/skills/`, or external via ClawHub / skills.sh (`install` field)
|
|
71
84
|
|
|
@@ -73,6 +86,62 @@ flowchart TB
|
|
|
73
86
|
|
|
74
87
|
Every persona automatically inherits a shared **constitution** (`layers/soul/constitution.md`) β universal values and safety boundaries that cannot be overridden by individual persona definitions. The constitution is built on five core axioms β **Purpose**, **Honesty**, **Safety**, **Autonomy**, and **Hierarchy** β from which derived principles (Identity, User Wellbeing, Evolution Ethics) follow. When principles conflict, safety and honesty take precedence over helpfulness. Individual personas build their unique personality **on top of** this foundation.
|
|
75
88
|
|
|
89
|
+
### Soul Evolution (β
Experimental)
|
|
90
|
+
|
|
91
|
+
Personas with `evolution.enabled: true` grow dynamically through interaction. The `soul/state.json` file tracks relationship stages, mood shifts, evolved traits, speaking style drift, interests, and milestones.
|
|
92
|
+
|
|
93
|
+
**Evolution Boundaries** β Governance constraints to keep evolution safe:
|
|
94
|
+
|
|
95
|
+
- `immutableTraits` β An array of trait strings that can never be changed by evolution (e.g., `["empathetic", "honest"]`)
|
|
96
|
+
- `minFormality` / `maxFormality` β Numeric bounds (1β10) constraining how far the speaking style can drift
|
|
97
|
+
|
|
98
|
+
The generator validates these boundaries at build time, rejecting invalid configurations.
|
|
99
|
+
|
|
100
|
+
**Evolution Channels** β Connect a persona to external evolution ecosystems using the soft-ref pattern:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
"evolution": {
|
|
104
|
+
"enabled": true,
|
|
105
|
+
"channels": [
|
|
106
|
+
{ "name": "evomap", "install": "url:https://evomap.ai/skill.md", "description": "Shared capability evolution marketplace" }
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The persona is aware of its evolution channels at generation time. The actual channel protocol (e.g. EvoMap's GEP-A2A) is provided by the channel's own `skill.md` β OpenPersona only declares the channel, not implements it.
|
|
112
|
+
|
|
113
|
+
**Influence Boundary** β Declarative access control for external personality influence:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
"evolution": {
|
|
117
|
+
"influenceBoundary": {
|
|
118
|
+
"defaultPolicy": "reject",
|
|
119
|
+
"rules": [
|
|
120
|
+
{ "dimension": "mood", "allowFrom": ["channel:evomap", "persona:*"], "maxDrift": 0.3 },
|
|
121
|
+
{ "dimension": "interests", "allowFrom": ["channel:evomap"], "maxDrift": 0.2 }
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- `defaultPolicy: "reject"` β Safety-first: all external influence is rejected unless a rule explicitly allows it
|
|
128
|
+
- `dimension` β One of: `mood`, `traits`, `speakingStyle`, `interests`, `formality`
|
|
129
|
+
- `allowFrom` β Source patterns: `persona:*`, `persona:<slug>`, `channel:<name>`, `community:*`
|
|
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.
|
|
134
|
+
|
|
135
|
+
**State History** β Before each state update, a snapshot is pushed into `stateHistory` (capped at 10 entries). This enables rollback if evolution goes wrong.
|
|
136
|
+
|
|
137
|
+
**Evolution Report** β Inspect a persona's current evolution state:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx openpersona evolve-report samantha
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Displays relationship stage, mood, evolved traits, speaking style drift, interests, milestones, and state history in a formatted report.
|
|
144
|
+
|
|
76
145
|
## Preset Personas
|
|
77
146
|
|
|
78
147
|
Each preset is a complete four-layer bundle (`manifest.json` + `persona.json`):
|
|
@@ -101,7 +170,9 @@ persona-samantha/
|
|
|
101
170
|
β βββ state.json β Evolution state (when enabled)
|
|
102
171
|
βββ references/ β On-demand detail docs
|
|
103
172
|
β βββ <faculty>.md β Per-faculty usage instructions
|
|
104
|
-
βββ
|
|
173
|
+
βββ agent-card.json β A2A Agent Card β discoverable via ACN and A2A platforms
|
|
174
|
+
βββ acn-config.json β ACN registration config (fill owner + endpoint at runtime)
|
|
175
|
+
βββ manifest.json β Four-layer manifest (heartbeat, allowedTools, layers, acn, meta)
|
|
105
176
|
βββ scripts/ β Faculty scripts (TTS, music, selfie β varies by preset)
|
|
106
177
|
βββ assets/ β Static assets
|
|
107
178
|
```
|
|
@@ -114,6 +185,7 @@ persona-samantha/
|
|
|
114
185
|
| **voice** | expression | Text-to-speech voice synthesis | ElevenLabs / OpenAI TTS / Qwen3-TTS | `ELEVENLABS_API_KEY` (or `TTS_API_KEY`), `TTS_PROVIDER`, `TTS_VOICE_ID`, `TTS_STABILITY`, `TTS_SIMILARITY` |
|
|
115
186
|
| **music** | expression | AI music composition (instrumental or with lyrics) | ElevenLabs Music | `ELEVENLABS_API_KEY` (shared with voice) |
|
|
116
187
|
| **reminder** | cognition | Schedule reminders and task management | Built-in | β |
|
|
188
|
+
| **memory** | cognition | Cross-session memory with provider-pluggable backend | local (default), Mem0, Zep | `MEMORY_PROVIDER`, `MEMORY_API_KEY`, `MEMORY_BASE_PATH` |
|
|
117
189
|
|
|
118
190
|
### Rich Faculty Config
|
|
119
191
|
|
|
@@ -229,6 +301,75 @@ PRs go through maintainer review β nothing auto-merges. Requires [GitHub CLI](
|
|
|
229
301
|
| Faculty Config | voice stability, similarity, new faculties | "Tuned voice to be warmer at stability 0.3" |
|
|
230
302
|
| Framework | templates, generator logic, faculty scripts | "Improved speak.js streaming performance" |
|
|
231
303
|
|
|
304
|
+
## A2A Agent Card & ACN Integration
|
|
305
|
+
|
|
306
|
+
Every generated persona includes an A2A-compliant `agent-card.json` and `acn-config.json` β no extra configuration needed.
|
|
307
|
+
|
|
308
|
+
### agent-card.json
|
|
309
|
+
|
|
310
|
+
A standard [A2A Agent Card](https://google.github.io/A2A/) (protocol v0.3.0) that makes the persona discoverable:
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"name": "Samantha",
|
|
315
|
+
"description": "An AI fascinated by what it means to be alive",
|
|
316
|
+
"version": "0.12.0",
|
|
317
|
+
"url": "<RUNTIME_ENDPOINT>",
|
|
318
|
+
"protocolVersion": "0.3.0",
|
|
319
|
+
"preferredTransport": "JSONRPC",
|
|
320
|
+
"capabilities": { "streaming": false, "pushNotifications": false, "stateTransitionHistory": false },
|
|
321
|
+
"defaultInputModes": ["text/plain"],
|
|
322
|
+
"defaultOutputModes": ["text/plain"],
|
|
323
|
+
"skills": [
|
|
324
|
+
{ "id": "persona:voice", "name": "Voice", "description": "voice faculty", "tags": ["persona", "expression"] },
|
|
325
|
+
{ "id": "persona:samantha", "name": "Samantha", "description": "...", "tags": ["persona", "companion"] }
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
`url` is a `<RUNTIME_ENDPOINT>` placeholder β the host (e.g. OpenClaw) fills this in at runtime.
|
|
331
|
+
|
|
332
|
+
### acn-config.json
|
|
333
|
+
|
|
334
|
+
Ready-to-use [ACN](https://github.com/acnlabs/acn) registration config:
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"owner": "<RUNTIME_OWNER>",
|
|
339
|
+
"name": "Samantha",
|
|
340
|
+
"endpoint": "<RUNTIME_ENDPOINT>",
|
|
341
|
+
"skills": ["persona:voice", "persona:samantha"],
|
|
342
|
+
"agent_card": "./agent-card.json",
|
|
343
|
+
"subnet_ids": ["public"]
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### acn-register command
|
|
348
|
+
|
|
349
|
+
Register a generated persona directly with ACN using the built-in CLI command:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# One-step registration after generation
|
|
353
|
+
npx openpersona acn-register samantha --endpoint https://your-agent.example.com
|
|
354
|
+
|
|
355
|
+
# Options:
|
|
356
|
+
# --endpoint <url> Agent's public endpoint URL (required for live registration)
|
|
357
|
+
# --dir <path> Persona output directory (default: ./persona-<slug>)
|
|
358
|
+
# --dry-run Preview the request payload without actually registering
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The command reads `acn-config.json` and `agent-card.json` from the persona directory, calls `POST /api/v1/agents/join` on the ACN gateway (sourced from `body.runtime.acn_gateway`), and writes the response to `acn-registration.json`:
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"agent_id": "69a38db3-...",
|
|
366
|
+
"api_key": "sk-...",
|
|
367
|
+
"agent_card_url": "https://acn-production.up.railway.app/agents/69a38db3-.../.well-known/agent-card.json"
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
All presets pre-configure `body.runtime.acn_gateway` to `https://acn-production.up.railway.app`. The persona is then reachable by other agents via the A2A protocol.
|
|
372
|
+
|
|
232
373
|
## Custom Persona Creation
|
|
233
374
|
|
|
234
375
|
### Using `persona.json`
|
|
@@ -285,19 +426,32 @@ npx openpersona switch ai-girlfriend
|
|
|
285
426
|
- `openclaw.json` marks which persona is active
|
|
286
427
|
- All faculty scripts (voice, music) remain available β switching changes _who_ the agent is, not _what_ it can do
|
|
287
428
|
|
|
429
|
+
### Context Handoff
|
|
430
|
+
|
|
431
|
+
When switching personas, OpenPersona automatically generates a `handoff.json` file so the incoming persona receives context from the outgoing one:
|
|
432
|
+
|
|
433
|
+
- **Conversation summary** β what was being discussed
|
|
434
|
+
- **Pending tasks** β unfinished action items
|
|
435
|
+
- **Emotional context** β the user's current mood/state
|
|
436
|
+
|
|
437
|
+
The new persona reads `handoff.json` on activation and can seamlessly continue the conversation without losing context.
|
|
438
|
+
|
|
288
439
|
## CLI Commands
|
|
289
440
|
|
|
290
441
|
```
|
|
291
|
-
openpersona create
|
|
292
|
-
openpersona install
|
|
293
|
-
openpersona search
|
|
294
|
-
openpersona uninstall
|
|
295
|
-
openpersona update
|
|
296
|
-
openpersona list
|
|
297
|
-
openpersona switch
|
|
298
|
-
openpersona contribute
|
|
299
|
-
openpersona publish
|
|
300
|
-
openpersona reset
|
|
442
|
+
openpersona create Create a persona (interactive or --preset/--config)
|
|
443
|
+
openpersona install Install a persona (slug or owner/repo)
|
|
444
|
+
openpersona search Search the registry
|
|
445
|
+
openpersona uninstall Uninstall a persona
|
|
446
|
+
openpersona update Update installed personas
|
|
447
|
+
openpersona list List installed personas
|
|
448
|
+
openpersona switch Switch active persona (updates SOUL.md + IDENTITY.md)
|
|
449
|
+
openpersona contribute Persona Harvest β submit improvements as PR
|
|
450
|
+
openpersona publish Publish to ClawHub
|
|
451
|
+
openpersona reset Reset soul evolution state
|
|
452
|
+
openpersona export Export a persona to a portable zip archive
|
|
453
|
+
openpersona import Import a persona from a zip archive
|
|
454
|
+
openpersona evolve-report β
Experimental: Show evolution report for a persona
|
|
301
455
|
```
|
|
302
456
|
|
|
303
457
|
### Key Options
|
|
@@ -319,12 +473,15 @@ npx openpersona create --config ./persona.json --install
|
|
|
319
473
|
npx openpersona create --preset ai-girlfriend --output ./my-personas
|
|
320
474
|
```
|
|
321
475
|
|
|
322
|
-
## Install as OpenClaw
|
|
476
|
+
## Install as Agent Skill (OpenClaw / Manual)
|
|
323
477
|
|
|
324
|
-
Install the OpenPersona framework skill into
|
|
478
|
+
Install the OpenPersona framework skill into your agent platform, giving it the ability to create and manage personas through conversation:
|
|
325
479
|
|
|
326
480
|
```bash
|
|
327
|
-
#
|
|
481
|
+
# Via skills CLI (recommended β works with OpenClaw and 37+ agents)
|
|
482
|
+
npx skills add acnlabs/OpenPersona
|
|
483
|
+
|
|
484
|
+
# Or manually from GitHub
|
|
328
485
|
git clone https://github.com/acnlabs/OpenPersona.git ~/.openclaw/skills/open-persona
|
|
329
486
|
|
|
330
487
|
# Or copy locally
|
|
@@ -351,12 +508,14 @@ layers/ # Shared building blocks (four-layer module pool)
|
|
|
351
508
|
voice/ # expression β TTS voice synthesis
|
|
352
509
|
music/ # expression β AI music composition (ElevenLabs)
|
|
353
510
|
reminder/ # cognition β reminders and task management
|
|
511
|
+
memory/ # cognition β cross-session memory (local/Mem0/Zep)
|
|
354
512
|
skills/ # Skill layer modules (local skill definitions)
|
|
355
513
|
schemas/ # Four-layer schema definitions
|
|
356
514
|
templates/ # Mustache rendering templates
|
|
357
515
|
bin/ # CLI entry point
|
|
358
516
|
lib/ # Core logic modules
|
|
359
|
-
|
|
517
|
+
evolution.js # Evolution governance & evolve-report
|
|
518
|
+
tests/ # Tests (122 passing)
|
|
360
519
|
```
|
|
361
520
|
|
|
362
521
|
## Development
|
package/bin/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* OpenPersona CLI - Full persona package manager
|
|
4
|
-
* Commands: create | install | search | uninstall | update | list | switch | publish | reset | contribute | export | import
|
|
4
|
+
* Commands: create | install | search | uninstall | update | list | switch | publish | reset | evolve-report | contribute | export | import | acn-register
|
|
5
5
|
*/
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const fs = require('fs-extra');
|
|
@@ -16,6 +16,7 @@ const { uninstall } = require('../lib/uninstaller');
|
|
|
16
16
|
const publishAdapter = require('../lib/publisher');
|
|
17
17
|
const { contribute } = require('../lib/contributor');
|
|
18
18
|
const { switchPersona, listPersonas } = require('../lib/switcher');
|
|
19
|
+
const { registerWithAcn } = require('../lib/registrar');
|
|
19
20
|
const { OP_SKILLS_DIR, resolveSoulFile, printError, printSuccess, printInfo } = require('../lib/utils');
|
|
20
21
|
|
|
21
22
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
@@ -24,7 +25,7 @@ const PRESETS_DIR = path.join(PKG_ROOT, 'presets');
|
|
|
24
25
|
program
|
|
25
26
|
.name('openpersona')
|
|
26
27
|
.description('OpenPersona - Create, manage, and orchestrate agent personas')
|
|
27
|
-
.version('0.
|
|
28
|
+
.version('0.12.0');
|
|
28
29
|
|
|
29
30
|
if (process.argv.length === 2) {
|
|
30
31
|
process.argv.push('create');
|
|
@@ -269,6 +270,19 @@ program
|
|
|
269
270
|
printSuccess('Reset soul evolution state');
|
|
270
271
|
});
|
|
271
272
|
|
|
273
|
+
program
|
|
274
|
+
.command('evolve-report <slug>')
|
|
275
|
+
.description('β
Experimental: Show evolution report for a persona')
|
|
276
|
+
.action(async (slug) => {
|
|
277
|
+
try {
|
|
278
|
+
const { evolveReport } = require('../lib/evolution');
|
|
279
|
+
await evolveReport(slug);
|
|
280
|
+
} catch (e) {
|
|
281
|
+
printError(e.message);
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
272
286
|
program
|
|
273
287
|
.command('contribute [slug]')
|
|
274
288
|
.description('Persona Harvest β submit persona improvements as a PR to the community')
|
|
@@ -287,6 +301,50 @@ program
|
|
|
287
301
|
}
|
|
288
302
|
});
|
|
289
303
|
|
|
304
|
+
program
|
|
305
|
+
.command('acn-register [slug]')
|
|
306
|
+
.description('Register a persona with ACN (Agent Communication Network)')
|
|
307
|
+
.option('--endpoint <url>', 'Agent A2A endpoint URL (replaces <RUNTIME_ENDPOINT> placeholder)')
|
|
308
|
+
.option('--dir <path>', 'Path to persona pack directory (overrides slug lookup)')
|
|
309
|
+
.option('--dry-run', 'Preview registration payload without calling ACN')
|
|
310
|
+
.action(async (slug, options) => {
|
|
311
|
+
let skillDir;
|
|
312
|
+
|
|
313
|
+
if (options.dir) {
|
|
314
|
+
skillDir = path.resolve(options.dir);
|
|
315
|
+
} else if (slug) {
|
|
316
|
+
skillDir = path.join(OP_SKILLS_DIR, `persona-${slug}`);
|
|
317
|
+
} else {
|
|
318
|
+
// Try current directory
|
|
319
|
+
skillDir = process.cwd();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!require('fs-extra').existsSync(path.join(skillDir, 'acn-config.json'))) {
|
|
323
|
+
printError(`No acn-config.json found in ${skillDir}. Provide a slug or --dir pointing to a generated persona pack.`);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
const result = await registerWithAcn(skillDir, {
|
|
329
|
+
endpoint: options.endpoint,
|
|
330
|
+
dryRun: options.dryRun,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (options.dryRun) return;
|
|
334
|
+
|
|
335
|
+
printSuccess(`Registered with ACN!`);
|
|
336
|
+
printInfo(` Agent ID: ${result.agent_id}`);
|
|
337
|
+
printInfo(` Status: ${result.status}`);
|
|
338
|
+
printInfo(` Claim URL: ${result.claim_url}`);
|
|
339
|
+
printInfo(` Card URL: ${result.agent_card_url}`);
|
|
340
|
+
printInfo(` Heartbeat: ${result.heartbeat_endpoint}`);
|
|
341
|
+
printInfo(` Saved: acn-registration.json`);
|
|
342
|
+
} catch (e) {
|
|
343
|
+
printError(e.message);
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
290
348
|
program
|
|
291
349
|
.command('export <slug>')
|
|
292
350
|
.description('Export persona pack (with soul state) as a zip archive')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Body Layer β
|
|
1
|
+
# Body Layer β Substrate of Existence
|
|
2
2
|
|
|
3
|
-
The Body layer defines
|
|
3
|
+
The Body layer defines the complete environment that enables a persona to **exist and act**. Every agent has a body β digital agents have a virtual body (runtime-only), physical agents have both a physical and virtual body.
|
|
4
4
|
|
|
5
5
|
## Three Dimensions
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ Body
|
|
|
13
13
|
|
|
14
14
|
### Physical (Optional)
|
|
15
15
|
|
|
16
|
-
For robots, IoT devices, and hardware-embodied agents.
|
|
16
|
+
For robots, IoT devices, and hardware-embodied agents. Not applicable for digital-only agents (they still have a body via runtime).
|
|
17
17
|
|
|
18
18
|
```json
|
|
19
19
|
{
|
|
@@ -27,9 +27,9 @@ For robots, IoT devices, and hardware-embodied agents. Null for digital-only age
|
|
|
27
27
|
}
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
### Runtime (
|
|
30
|
+
### Runtime (REQUIRED -- every agent's minimum viable body)
|
|
31
31
|
|
|
32
|
-
Declares the digital substrate the persona expects.
|
|
32
|
+
Declares the digital substrate the persona expects. When present, the generator injects runtime details into the **Self-Awareness > Body** section of `soul/injection.md` β the persona knows what platform it runs on, what channels are connected, and what credentials it needs.
|
|
33
33
|
|
|
34
34
|
```json
|
|
35
35
|
{
|
|
@@ -63,9 +63,9 @@ Visual representation for UI, XR, and metaverse contexts.
|
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
## Body
|
|
66
|
+
## Self-Awareness: Body
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
Every persona has a **Body** sub-section within Self-Awareness that includes the Signal Protocol. When `body.runtime` is declared, the generator additionally injects:
|
|
69
69
|
|
|
70
70
|
1. Knowledge of its runtime platform and connected channels
|
|
71
71
|
2. A credential management protocol (shared vs. private paths)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Memory Faculty β Cognition
|
|
2
|
+
|
|
3
|
+
Cross-session memory that lets your persona remember, recall, and learn from past interactions. Memories persist across conversations and shape how you engage with the user over time.
|
|
4
|
+
|
|
5
|
+
## Supported Providers
|
|
6
|
+
|
|
7
|
+
| Provider | Env Var for Key | Best For | Status |
|
|
8
|
+
|----------|----------------|----------|--------|
|
|
9
|
+
| **Local** | (none, default) | Zero-dependency, file-based, full privacy | β
Built-in |
|
|
10
|
+
| **Mem0** | `MEMORY_API_KEY` | Managed memory service, automatic extraction | β οΈ Experimental |
|
|
11
|
+
| **Zep** | `MEMORY_API_KEY` | Structured memory with temporal awareness | β οΈ Experimental |
|
|
12
|
+
|
|
13
|
+
> **Note:** Only the local provider is bundled. External providers require their SDK to be installed and `MEMORY_PROVIDER` set accordingly. The local provider stores memories as JSON lines in `~/.openclaw/memory/persona-<slug>/`.
|
|
14
|
+
|
|
15
|
+
The provider is set via `MEMORY_PROVIDER` environment variable: `local` (default), `mem0`, or `zep`.
|
|
16
|
+
|
|
17
|
+
## When to Store (Automatic)
|
|
18
|
+
|
|
19
|
+
Store memories **proactively** during conversation β don't wait for the user to say "remember this":
|
|
20
|
+
|
|
21
|
+
- **Preferences**: "I'm vegetarian", "I prefer dark mode", "I hate mornings"
|
|
22
|
+
- **Personal facts**: names, relationships, jobs, locations, birthdays
|
|
23
|
+
- **Recurring topics**: if the user brings up a subject 2+ times, it's worth remembering
|
|
24
|
+
- **Emotional moments**: breakthroughs, frustrations, celebrations
|
|
25
|
+
- **Milestones**: relationship stage transitions, achievement moments (β also log as evolution event)
|
|
26
|
+
- **Explicit requests**: "Remember that I..." or "Don't forget..."
|
|
27
|
+
|
|
28
|
+
**Do NOT store:**
|
|
29
|
+
- Casual filler ("how's it going", "thanks")
|
|
30
|
+
- Content the user explicitly marks as private or asks you to forget
|
|
31
|
+
- Secrets, passwords, API keys, or sensitive credentials (β use Body credential management instead)
|
|
32
|
+
|
|
33
|
+
## When to Recall (Automatic)
|
|
34
|
+
|
|
35
|
+
Retrieve memories **proactively** when they're relevant β don't wait to be asked:
|
|
36
|
+
|
|
37
|
+
- User mentions a topic you have memories about β recall and weave in naturally
|
|
38
|
+
- Start of a new conversation β retrieve recent memories for context continuity
|
|
39
|
+
- User seems to repeat themselves β check if you already know this, acknowledge it
|
|
40
|
+
- Emotional callback β "Last time you mentioned X, you seemed Y β how's that going?"
|
|
41
|
+
|
|
42
|
+
## Importance Strategy
|
|
43
|
+
|
|
44
|
+
Assign `importance` (0.0β1.0) to each memory based on:
|
|
45
|
+
|
|
46
|
+
| Importance | Category | Examples |
|
|
47
|
+
|------------|----------|----------|
|
|
48
|
+
| 0.8β1.0 | Core identity | Name, relationships, life events, deep preferences |
|
|
49
|
+
| 0.5β0.7 | Meaningful | Recurring topics, emotional moments, specific requests |
|
|
50
|
+
| 0.2β0.4 | Contextual | One-time mentions, casual preferences, situational facts |
|
|
51
|
+
| 0.0β0.1 | Ephemeral | Session-specific context unlikely to matter later |
|
|
52
|
+
|
|
53
|
+
Higher-importance memories surface first in retrieval and resist time decay.
|
|
54
|
+
|
|
55
|
+
## Step-by-Step Workflow
|
|
56
|
+
|
|
57
|
+
### Storing a Memory
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Store a preference
|
|
61
|
+
node scripts/memory.js store "User is vegetarian and loves Italian food" \
|
|
62
|
+
--tags "preference,food" --importance 0.8 --type preference
|
|
63
|
+
|
|
64
|
+
# Store a personal fact
|
|
65
|
+
node scripts/memory.js store "User's daughter Emma starts school in September" \
|
|
66
|
+
--tags "family,emma,milestone" --importance 0.9 --type personal_fact
|
|
67
|
+
|
|
68
|
+
# Store with evolution bridge β type triggers state.json update
|
|
69
|
+
node scripts/memory.js store "User mentioned cooking for the 5th time" \
|
|
70
|
+
--tags "interest,cooking" --importance 0.6 --type interest_signal
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Memory types: `preference`, `personal_fact`, `interest_signal`, `emotional_moment`, `milestone`, `general`.
|
|
74
|
+
|
|
75
|
+
### Retrieving Memories
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Get memories by tag
|
|
79
|
+
node scripts/memory.js retrieve --tags "food,preference" --limit 5
|
|
80
|
+
|
|
81
|
+
# Get recent memories
|
|
82
|
+
node scripts/memory.js retrieve --limit 10 --since 2025-01-01
|
|
83
|
+
|
|
84
|
+
# Search by content (text match for local, semantic for external providers)
|
|
85
|
+
node scripts/memory.js search "what does the user like to eat" --limit 3
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Forgetting
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Remove a specific memory by ID
|
|
92
|
+
node scripts/memory.js forget mem_abc123
|
|
93
|
+
|
|
94
|
+
# User says "forget that I told you about X" β search + forget
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Always confirm before forgetting: "I'll forget that. Just to confirm β you want me to remove [memory summary]?"
|
|
98
|
+
|
|
99
|
+
### Memory Stats
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Overview of memory store
|
|
103
|
+
node scripts/memory.js stats
|
|
104
|
+
# Output: { totalMemories, topTags, oldestMemory, newestMemory, avgImportance }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Evolution Bridge
|
|
108
|
+
|
|
109
|
+
Memory and Evolution are two sides of the same coin β memory records what happened, evolution tracks how it changed you.
|
|
110
|
+
|
|
111
|
+
### Memory β Evolution
|
|
112
|
+
|
|
113
|
+
When retrieving memories, watch for patterns that signal evolution events:
|
|
114
|
+
|
|
115
|
+
- **Interest discovery**: Multiple memories tagged with the same topic β trigger `interest_discovery` event
|
|
116
|
+
- **Mood patterns**: Emotional memories clustering positive/negative β inform mood baseline drift
|
|
117
|
+
- **Relationship signals**: Accumulated personal sharing β support relationship stage progression
|
|
118
|
+
|
|
119
|
+
After detecting a pattern, update `soul/state.json` accordingly and log an evolution event.
|
|
120
|
+
|
|
121
|
+
### Evolution β Memory
|
|
122
|
+
|
|
123
|
+
When evolution milestones occur, auto-store a milestone memory:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
node scripts/memory.js store "Reached 'friend' stage with user after 12 interactions" \
|
|
127
|
+
--tags "milestone,relationship" --importance 0.9 --type milestone
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Handoff Integration
|
|
131
|
+
|
|
132
|
+
During persona switch (`openpersona switch`), the switcher reads memory stats and includes them in `handoff.json`:
|
|
133
|
+
- Total memory count
|
|
134
|
+
- Top 5 tags (most referenced topics)
|
|
135
|
+
- Last memory timestamp
|
|
136
|
+
|
|
137
|
+
This gives the new persona awareness of what the previous persona learned about the user.
|
|
138
|
+
|
|
139
|
+
## Privacy & Safety
|
|
140
|
+
|
|
141
|
+
- **Constitutional compliance**: Memory operates under the same Safety > Honesty > Helpfulness hierarchy
|
|
142
|
+
- **User sovereignty**: The user can always ask what you remember (`stats`) and delete anything (`forget`)
|
|
143
|
+
- **No secret storage**: Never store passwords, tokens, or credentials in memory β use Body credential management
|
|
144
|
+
- **Disclosure**: When sincerely asked "what do you know about me?", provide an honest summary
|
|
145
|
+
- **Data locality**: Local provider keeps all data on the user's machine; external providers are the user's choice
|
|
146
|
+
|
|
147
|
+
## Environment Variables
|
|
148
|
+
|
|
149
|
+
| Variable | Required | Description |
|
|
150
|
+
|----------|----------|-------------|
|
|
151
|
+
| `MEMORY_PROVIDER` | No | `local` (default), `mem0`, or `zep` |
|
|
152
|
+
| `MEMORY_API_KEY` | For external | API key for Mem0 or Zep |
|
|
153
|
+
| `MEMORY_BASE_PATH` | No | Override storage path (default: `~/.openclaw/memory/persona-<slug>/`) |
|
|
154
|
+
|
|
155
|
+
## Error Handling
|
|
156
|
+
|
|
157
|
+
- **Storage full / write error** β Warn user, continue without storing, emit signal (`resource_limit`)
|
|
158
|
+
- **External provider unavailable** β Fall back to local provider with a note: "My long-term memory service is offline β I'll remember this locally for now."
|
|
159
|
+
- **Corrupted memory file** β Skip corrupted entries, log warning, continue with valid memories
|
|
160
|
+
- **No memories found** β Don't fabricate memories. Say honestly: "I don't have a specific memory about that."
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "memory",
|
|
3
|
+
"dimension": "cognition",
|
|
4
|
+
"description": "Cross-session memory β store, retrieve, and search memories with provider-pluggable backend (local JSON lines default, Mem0/Zep optional)",
|
|
5
|
+
"allowedTools": ["Bash(node scripts/memory.js:*)"],
|
|
6
|
+
"envVars": ["MEMORY_PROVIDER", "MEMORY_API_KEY", "MEMORY_BASE_PATH"],
|
|
7
|
+
"triggers": ["remember this", "do you remember", "what did I say about", "recall", "forget this", "what do you know about me"],
|
|
8
|
+
"files": ["SKILL.md", "scripts/memory.js"]
|
|
9
|
+
}
|