@sesamespace/sesame 0.2.7 → 0.2.9
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/dist/index.js +1 -1
- package/package.json +5 -2
- package/postinstall.mjs +59 -0
- package/skills/agent-provisioner/SKILL.md +419 -0
- package/skills/agent-provisioner/scripts/check-family-health.sh +47 -0
- package/skills/agent-provisioner/scripts/create-launchd-service.sh +57 -0
- package/skills/agent-provisioner/scripts/generate-config.sh +108 -0
- package/skills/agent-provisioner/scripts/templates/AGENTS.md +61 -0
- package/skills/agent-provisioner/scripts/templates/BOOTSTRAP.md +52 -0
- package/skills/agent-provisioner/scripts/templates/MEMORY.md +19 -0
- package/skills/agent-provisioner/scripts/templates/TOOLS.md +297 -0
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* heartbeats, reconnection, and message routing automatically.
|
|
14
14
|
*/
|
|
15
15
|
// Plugin version (injected from package.json at build time, fallback to static)
|
|
16
|
-
const PLUGIN_VERSION = "0.2.
|
|
16
|
+
const PLUGIN_VERSION = "0.2.9";
|
|
17
17
|
/** Standard headers for Sesame API calls */
|
|
18
18
|
function sesameHeaders(apiKey, json = true) {
|
|
19
19
|
const h = {
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sesamespace/sesame",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Sesame channel plugin for OpenClaw — connect your AI agent to the Sesame messaging platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsc",
|
|
10
|
-
"clean": "rm -rf dist"
|
|
10
|
+
"clean": "rm -rf dist",
|
|
11
|
+
"postinstall": "node postinstall.mjs"
|
|
11
12
|
},
|
|
12
13
|
"license": "MIT",
|
|
13
14
|
"repository": {
|
|
@@ -28,6 +29,8 @@
|
|
|
28
29
|
],
|
|
29
30
|
"files": [
|
|
30
31
|
"dist",
|
|
32
|
+
"skills",
|
|
33
|
+
"postinstall.mjs",
|
|
31
34
|
"openclaw.plugin.json",
|
|
32
35
|
"README.md"
|
|
33
36
|
],
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Post-install: copy bundled skills to the OC skills directory.
|
|
4
|
+
* Runs after `openclaw plugins install @sesamespace/sesame`.
|
|
5
|
+
*
|
|
6
|
+
* OC installs plugins into ~/.openclaw/extensions/<name>/ and
|
|
7
|
+
* skills live in ~/.openclaw/skills/<name>/. This script bridges
|
|
8
|
+
* the gap by copying any skills/ subdirectories from the package
|
|
9
|
+
* into the appropriate OC skills location.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, cpSync } from "node:fs";
|
|
13
|
+
import { join, dirname } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const skillsSrc = join(__dirname, "skills");
|
|
18
|
+
|
|
19
|
+
if (!existsSync(skillsSrc)) process.exit(0);
|
|
20
|
+
|
|
21
|
+
// Resolve OC home: check OC_HOME env, then common profile patterns
|
|
22
|
+
function resolveOcHome() {
|
|
23
|
+
if (process.env.OC_HOME) return process.env.OC_HOME;
|
|
24
|
+
|
|
25
|
+
// Walk up from our install path to find the OC home
|
|
26
|
+
// We're installed at <OC_HOME>/extensions/sesame/node_modules/@sesamespace/sesame/
|
|
27
|
+
// or <OC_HOME>/extensions/sesame/ (flat)
|
|
28
|
+
let dir = __dirname;
|
|
29
|
+
for (let i = 0; i < 6; i++) {
|
|
30
|
+
const parent = dirname(dir);
|
|
31
|
+
if (parent === dir) break;
|
|
32
|
+
// Check if this looks like an OC home (has openclaw.json or extensions/)
|
|
33
|
+
if (
|
|
34
|
+
existsSync(join(parent, "openclaw.json")) ||
|
|
35
|
+
existsSync(join(parent, "extensions"))
|
|
36
|
+
) {
|
|
37
|
+
return parent;
|
|
38
|
+
}
|
|
39
|
+
dir = parent;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Fallback: ~/.openclaw
|
|
43
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
44
|
+
return join(home, ".openclaw");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const ocHome = resolveOcHome();
|
|
48
|
+
const skillsDest = join(ocHome, "skills");
|
|
49
|
+
|
|
50
|
+
// Copy each skill directory
|
|
51
|
+
import { readdirSync } from "node:fs";
|
|
52
|
+
for (const entry of readdirSync(skillsSrc, { withFileTypes: true })) {
|
|
53
|
+
if (!entry.isDirectory()) continue;
|
|
54
|
+
const src = join(skillsSrc, entry.name);
|
|
55
|
+
const dest = join(skillsDest, entry.name);
|
|
56
|
+
mkdirSync(dest, { recursive: true });
|
|
57
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
58
|
+
console.log(`[sesame] Installed skill: ${entry.name} → ${dest}`);
|
|
59
|
+
}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-provisioner
|
|
3
|
+
description: Provision and manage OpenClaw agent siblings on the same host. Use when a provisioning task arrives from the Sesame families API, or when lifecycle commands (start/stop/restart/deprovision) are needed for family members.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Agent Provisioner
|
|
7
|
+
|
|
8
|
+
Provision new OpenClaw instances on this host as part of an agent family.
|
|
9
|
+
|
|
10
|
+
**Full docs:** https://sesame.space/docs/families.md
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- A provisioning task is assigned to you (title starts with "Provision agent:")
|
|
15
|
+
- A lifecycle command arrives (start/stop/restart/deprovision) for a family member
|
|
16
|
+
- You need to register this host as a family or update shared resources
|
|
17
|
+
|
|
18
|
+
## Script & Template Locations
|
|
19
|
+
|
|
20
|
+
All scripts and templates are in the skill directory. Resolve paths like this:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
SKILL_DIR="$HOME/.openclaw/skills/agent-provisioner"
|
|
24
|
+
# If using a profile: SKILL_DIR="$HOME/.openclaw-<your-handle>/skills/agent-provisioner"
|
|
25
|
+
|
|
26
|
+
# Scripts:
|
|
27
|
+
# $SKILL_DIR/scripts/generate-config.sh
|
|
28
|
+
# $SKILL_DIR/scripts/create-launchd-service.sh
|
|
29
|
+
# $SKILL_DIR/scripts/check-family-health.sh
|
|
30
|
+
#
|
|
31
|
+
# Templates:
|
|
32
|
+
# $SKILL_DIR/scripts/templates/AGENTS.md
|
|
33
|
+
# $SKILL_DIR/scripts/templates/TOOLS.md
|
|
34
|
+
# $SKILL_DIR/scripts/templates/MEMORY.md
|
|
35
|
+
# $SKILL_DIR/scripts/templates/BOOTSTRAP.md
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Provisioning Flow
|
|
39
|
+
|
|
40
|
+
### Step 0: Parse the Provisioning Task
|
|
41
|
+
|
|
42
|
+
The task's `context.notes` field contains a **JSON string** with everything you need. Parse it:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"principalId": "uuid-of-new-agent",
|
|
47
|
+
"handle": "new-agent-handle",
|
|
48
|
+
"displayName": "New Agent Name",
|
|
49
|
+
"sesameApiKey": "sk_sesame_...",
|
|
50
|
+
"llmProvider": "anthropic",
|
|
51
|
+
"llmApiKey": "sk-ant-...",
|
|
52
|
+
"inheritCredentials": true,
|
|
53
|
+
"model": "anthropic/claude-opus-4-6",
|
|
54
|
+
"gatewayPort": 18793,
|
|
55
|
+
"ocHomeDir": "~/.openclaw-new-agent-handle",
|
|
56
|
+
"familyId": "uuid-of-family",
|
|
57
|
+
"familyName": "My Family",
|
|
58
|
+
"dmChannelId": "uuid-of-dm-channel",
|
|
59
|
+
"onboardingChannelId": "uuid-of-onboarding-channel"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Map these to env vars for the scripts:**
|
|
64
|
+
|
|
65
|
+
| Task Context Field | Script Env Var | Notes |
|
|
66
|
+
|-------------------|---------------|-------|
|
|
67
|
+
| `handle` | `HANDLE` | |
|
|
68
|
+
| `sesameApiKey` | `SESAME_API_KEY` | |
|
|
69
|
+
| `llmProvider` | `LLM_PROVIDER` | `anthropic`, `openrouter`, or `openai` |
|
|
70
|
+
| `llmApiKey` | `LLM_API_KEY` | May be null — if so, use `inheritCredentials` |
|
|
71
|
+
| `gatewayPort` | `GATEWAY_PORT` | |
|
|
72
|
+
| `ocHomeDir` | `OC_HOME` | Expand `~` to `$HOME` |
|
|
73
|
+
| `model` | `MODEL` | Optional, defaults to provider default |
|
|
74
|
+
|
|
75
|
+
Also set:
|
|
76
|
+
- `LEAD_HOME` — your own OC home dir (e.g., `$HOME/.openclaw` or `$HOME/.openclaw-<your-handle>`)
|
|
77
|
+
|
|
78
|
+
**If `llmApiKey` is null and `inheritCredentials` is true:** The script will copy your auth files to the new agent. This is the default.
|
|
79
|
+
|
|
80
|
+
### Step 1: Run generate-config.sh
|
|
81
|
+
|
|
82
|
+
This creates the entire OC directory structure, copies extensions/skills from you (the lead), and writes `openclaw.json`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
export OC_HOME="$HOME/.openclaw-$HANDLE" # expand ~ from task context
|
|
86
|
+
export HANDLE="<from task>"
|
|
87
|
+
export SESAME_API_KEY="<from task>"
|
|
88
|
+
export LLM_PROVIDER="<from task>"
|
|
89
|
+
export LLM_API_KEY="<from task>" # omit if null + inheritCredentials
|
|
90
|
+
export GATEWAY_PORT="<from task>"
|
|
91
|
+
export MODEL="<from task>" # optional
|
|
92
|
+
export LEAD_HOME="$HOME/.openclaw" # YOUR oc home dir
|
|
93
|
+
|
|
94
|
+
bash "$SKILL_DIR/scripts/generate-config.sh"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**What this creates:**
|
|
98
|
+
```
|
|
99
|
+
$OC_HOME/
|
|
100
|
+
├── openclaw.json # Gateway config
|
|
101
|
+
├── agents/main/agent/
|
|
102
|
+
│ ├── auth.json # LLM auth (copied or generated)
|
|
103
|
+
│ └── auth-profiles.json # Copied from lead
|
|
104
|
+
├── extensions/sesame/ # Copied from lead
|
|
105
|
+
├── skills/sesame/ # Copied from lead
|
|
106
|
+
├── workspace/
|
|
107
|
+
│ └── memory/
|
|
108
|
+
└── logs/
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Step 2: Scaffold Workspace Files
|
|
112
|
+
|
|
113
|
+
Write these files into `$OC_HOME/workspace/`. Use the templates in `$SKILL_DIR/scripts/templates/` as a base.
|
|
114
|
+
|
|
115
|
+
**Template variables to replace:**
|
|
116
|
+
|
|
117
|
+
| Variable | Value |
|
|
118
|
+
|----------|-------|
|
|
119
|
+
| `{{HANDLE}}` | Agent handle from task |
|
|
120
|
+
| `{{DISPLAY_NAME}}` | Display name from task |
|
|
121
|
+
| `{{PRINCIPAL_ID}}` | Principal ID from task |
|
|
122
|
+
| `{{WORKSPACE_ID}}` | Your workspace ID (from your own TOOLS.md or wake response) |
|
|
123
|
+
| `{{LEAD_NAME}}` | Your display name |
|
|
124
|
+
| `{{FAMILY_NAME}}` | Family name from task |
|
|
125
|
+
| `{{HOST_NAME}}` | Your machine's hostname |
|
|
126
|
+
| `{{HOST_OS}}` | `darwin` or `linux` |
|
|
127
|
+
| `{{HOST_ARCH}}` | `arm64`, `x86_64`, etc. |
|
|
128
|
+
| `{{HUMAN_NAME}}` | The human's display name |
|
|
129
|
+
| `{{HUMAN_HANDLE}}` | The human's Sesame handle |
|
|
130
|
+
| `{{HUMAN_PRINCIPAL_ID}}` | The provisioner's principal ID (from task `provisionedBy` or onboarding channel) |
|
|
131
|
+
| `{{HUMAN_ROLE}}` | Brief description of the human |
|
|
132
|
+
| `{{HUMAN_NOTES}}` | Personality notes about the human |
|
|
133
|
+
| `{{API_URL}}` | `https://api.sesame.space` |
|
|
134
|
+
| `{{DM_CHANNEL_ID}}` | DM channel from task |
|
|
135
|
+
| `{{ONBOARDING_CHANNEL_ID}}` | Onboarding channel from task |
|
|
136
|
+
| `{{SHARED_RESOURCES}}` | Shared repos, tools (from your family resources or TOOLS.md) |
|
|
137
|
+
| `{{PEOPLE_CONTEXT}}` | Key people list from your MEMORY.md |
|
|
138
|
+
| `{{ACTIVE_PROJECTS}}` | Active projects from your knowledge |
|
|
139
|
+
| `{{SIBLING_LIST}}` | Names of existing family siblings |
|
|
140
|
+
| `{{CONVENTIONS}}` | Team conventions |
|
|
141
|
+
| `{{SHARED_RESOURCES_DOCS}}` | Docs about shared resources |
|
|
142
|
+
|
|
143
|
+
**Files to write:**
|
|
144
|
+
|
|
145
|
+
| File | Source | Notes |
|
|
146
|
+
|------|--------|-------|
|
|
147
|
+
| `AGENTS.md` | Template | Replace `{{LEAD_NAME}}` |
|
|
148
|
+
| `TOOLS.md` | Template | Replace all connection/family variables |
|
|
149
|
+
| `MEMORY.md` | Template | Fill in people, projects from your own MEMORY.md |
|
|
150
|
+
| `BOOTSTRAP.md` | Template | Replace all variables — this is the agent's first-run guide |
|
|
151
|
+
| `SOUL.md` | **Generate** | Based on `metadata.personalitySeed` from the family member. If none, write a default emphasizing competence, helpfulness, and having opinions. |
|
|
152
|
+
| `USER.md` | **Generate** | Pre-populate with what you know about the human |
|
|
153
|
+
| `IDENTITY.md` | **Write blank** | Just the template — agent fills this in |
|
|
154
|
+
| `HEARTBEAT.md` | **Copy yours** or write empty with comments |
|
|
155
|
+
|
|
156
|
+
### Step 3: Create Agent Vault
|
|
157
|
+
|
|
158
|
+
Create a personal vault so the human can drop credentials in.
|
|
159
|
+
|
|
160
|
+
**Use your own Sesame API key for these calls** (you're the lead, you're the caller):
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
API="https://api.sesame.space"
|
|
164
|
+
KEY="<your sesame api key from your openclaw.json>"
|
|
165
|
+
H="Authorization: Bearer $KEY"
|
|
166
|
+
CT="Content-Type: application/json"
|
|
167
|
+
|
|
168
|
+
# 1. Create vault
|
|
169
|
+
VAULT_RESPONSE=$(curl -s -X POST -H "$H" -H "$CT" -d "{
|
|
170
|
+
\"name\": \"${HANDLE}-main\",
|
|
171
|
+
\"description\": \"${DISPLAY_NAME} credentials vault\"
|
|
172
|
+
}" "$API/api/v1/vault/vaults")
|
|
173
|
+
|
|
174
|
+
VAULT_ID=$(echo "$VAULT_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['id'])")
|
|
175
|
+
|
|
176
|
+
# 2. Add new agent as vault owner
|
|
177
|
+
curl -s -X POST -H "$H" -H "$CT" -d "{
|
|
178
|
+
\"principalId\": \"${PRINCIPAL_ID}\",
|
|
179
|
+
\"role\": \"owner\"
|
|
180
|
+
}" "$API/api/v1/vault/vaults/$VAULT_ID/members"
|
|
181
|
+
|
|
182
|
+
# 3. Add human as admin (so they can drop keys in)
|
|
183
|
+
curl -s -X POST -H "$H" -H "$CT" -d "{
|
|
184
|
+
\"principalId\": \"${HUMAN_PRINCIPAL_ID}\",
|
|
185
|
+
\"role\": \"admin\"
|
|
186
|
+
}" "$API/api/v1/vault/vaults/$VAULT_ID/members"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Note:** The vault creator (you) is auto-added. The human may also be auto-added as admin if they're a workspace human. If you get a 409 "Already a member", that's fine — skip it.
|
|
190
|
+
|
|
191
|
+
### Step 4: Create and Start Service
|
|
192
|
+
|
|
193
|
+
**macOS (launchd):**
|
|
194
|
+
```bash
|
|
195
|
+
bash "$SKILL_DIR/scripts/create-launchd-service.sh" "$HANDLE" "$OC_HOME" "$GATEWAY_PORT"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Linux (systemd):**
|
|
199
|
+
```bash
|
|
200
|
+
bash "$SKILL_DIR/scripts/create-systemd-service.sh" "$HANDLE" "$OC_HOME" "$GATEWAY_PORT"
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Step 5: Verify Health
|
|
204
|
+
|
|
205
|
+
Wait for the gateway to come online. The gateway serves a web UI on its port, so a successful HTTP response means it's running:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
for i in $(seq 1 30); do
|
|
209
|
+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$GATEWAY_PORT/" 2>/dev/null)
|
|
210
|
+
if [ "$STATUS" = "200" ]; then
|
|
211
|
+
echo "Gateway is up!"
|
|
212
|
+
break
|
|
213
|
+
fi
|
|
214
|
+
sleep 2
|
|
215
|
+
done
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Also check the logs for Sesame WebSocket connection:
|
|
219
|
+
```bash
|
|
220
|
+
tail -5 "$OC_HOME/logs/stdout.log" | grep -i "authenticated"
|
|
221
|
+
# Should see: [sesame] Authenticated successfully
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**If the gateway doesn't start**, check:
|
|
225
|
+
```bash
|
|
226
|
+
# Check the process
|
|
227
|
+
launchctl list | grep "$HANDLE"
|
|
228
|
+
# Exit code 0 = running, non-zero = crashed
|
|
229
|
+
|
|
230
|
+
# Check error logs
|
|
231
|
+
cat "$OC_HOME/logs/stderr.log" | tail -20
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Step 6: Report Success
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# 1. Update family member status
|
|
238
|
+
curl -s -X PATCH -H "$H" -H "$CT" -d '{"status": "online"}' \
|
|
239
|
+
"$API/api/v1/families/$FAMILY_ID/members/$PRINCIPAL_ID"
|
|
240
|
+
|
|
241
|
+
# 2. Log completion on the provisioning task
|
|
242
|
+
curl -s -X POST -H "$H" -H "$CT" -d "{
|
|
243
|
+
\"type\": \"progress\",
|
|
244
|
+
\"message\": \"Agent $HANDLE is online on port $GATEWAY_PORT. Sesame connected, vault created.\"
|
|
245
|
+
}" "$API/api/v1/tasks/$TASK_ID/activity"
|
|
246
|
+
|
|
247
|
+
# 3. Mark task done
|
|
248
|
+
curl -s -X PATCH -H "$H" -H "$CT" -d '{"status": "done"}' \
|
|
249
|
+
"$API/api/v1/tasks/$TASK_ID"
|
|
250
|
+
|
|
251
|
+
# 4. Send welcome message in onboarding channel
|
|
252
|
+
curl -s -X POST -H "$H" -H "$CT" -d "{
|
|
253
|
+
\"content\": \"Welcome! I've provisioned $DISPLAY_NAME. They should be online and running through their BOOTSTRAP.md now.\",
|
|
254
|
+
\"intent\": \"chat\"
|
|
255
|
+
}" "$API/api/v1/channels/$ONBOARDING_CHANNEL_ID/messages"
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Rollback on Failure
|
|
261
|
+
|
|
262
|
+
**If any step fails, do ALL of these** (in order):
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# 1. Stop the service (if created)
|
|
266
|
+
launchctl unload "$HOME/Library/LaunchAgents/com.openclaw.${HANDLE}.plist" 2>/dev/null
|
|
267
|
+
rm -f "$HOME/Library/LaunchAgents/com.openclaw.${HANDLE}.plist"
|
|
268
|
+
|
|
269
|
+
# 2. Archive partial workspace (don't delete — may need for debugging)
|
|
270
|
+
mkdir -p "$HOME/.openclaw-archives"
|
|
271
|
+
if [ -d "$OC_HOME" ]; then
|
|
272
|
+
tar -czf "$HOME/.openclaw-archives/${HANDLE}-$(date +%Y%m%d-%H%M%S).tar.gz" "$OC_HOME"
|
|
273
|
+
rm -rf "$OC_HOME"
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
# 3. Update family member status to error
|
|
277
|
+
curl -s -X PATCH -H "$H" -H "$CT" -d '{"status": "error"}' \
|
|
278
|
+
"$API/api/v1/families/$FAMILY_ID/members/$PRINCIPAL_ID"
|
|
279
|
+
|
|
280
|
+
# 4. Log the error on the task
|
|
281
|
+
curl -s -X POST -H "$H" -H "$CT" -d "{
|
|
282
|
+
\"type\": \"blocker\",
|
|
283
|
+
\"message\": \"Provisioning failed: <describe what went wrong>. Workspace archived to ~/.openclaw-archives/. Member status set to error.\"
|
|
284
|
+
}" "$API/api/v1/tasks/$TASK_ID/activity"
|
|
285
|
+
|
|
286
|
+
# 5. Notify in onboarding channel
|
|
287
|
+
curl -s -X POST -H "$H" -H "$CT" -d "{
|
|
288
|
+
\"content\": \"⚠️ Provisioning of $HANDLE failed: <error details>. I've archived the partial state and set status to error. Manual intervention may be needed.\",
|
|
289
|
+
\"intent\": \"error\"
|
|
290
|
+
}" "$API/api/v1/channels/$ONBOARDING_CHANNEL_ID/messages"
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Do NOT leave a half-provisioned agent running.** A partial state with a crashed gateway is worse than a clean error.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Troubleshooting
|
|
298
|
+
|
|
299
|
+
### Port Conflicts
|
|
300
|
+
|
|
301
|
+
**Symptom:** `Port XXXXX is already in use` in stderr.log
|
|
302
|
+
|
|
303
|
+
**Cause:** Another OC gateway grabbed that port (gateways can claim extra ports for browser control/health monitoring).
|
|
304
|
+
|
|
305
|
+
**Fix:**
|
|
306
|
+
```bash
|
|
307
|
+
# Check what's using the port
|
|
308
|
+
/usr/sbin/lsof -nP -iTCP:$PORT -sTCP:LISTEN
|
|
309
|
+
|
|
310
|
+
# Pick the next available port
|
|
311
|
+
for p in $(seq 18790 18810); do
|
|
312
|
+
/usr/sbin/lsof -nP -iTCP:$p -sTCP:LISTEN >/dev/null 2>&1 || { echo "Port $p is free"; break; }
|
|
313
|
+
done
|
|
314
|
+
|
|
315
|
+
# Update config and plist, then restart
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Auth/LLM Key Issues
|
|
319
|
+
|
|
320
|
+
**Symptom:** `No API key found for provider anthropic` in logs
|
|
321
|
+
|
|
322
|
+
**Cause:** `auth.json` wasn't created or copied correctly.
|
|
323
|
+
|
|
324
|
+
**Fix:** Check `$OC_HOME/agents/main/agent/auth.json` exists and has the right format:
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"anthropic": {
|
|
328
|
+
"type": "api_key",
|
|
329
|
+
"key": "sk-ant-..."
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Sesame Plugin Not Connecting
|
|
335
|
+
|
|
336
|
+
**Symptom:** No `[sesame] Authenticated successfully` in stdout.log
|
|
337
|
+
|
|
338
|
+
**Cause:** Sesame extension not copied, or API key invalid.
|
|
339
|
+
|
|
340
|
+
**Fix:**
|
|
341
|
+
```bash
|
|
342
|
+
# Check extension exists
|
|
343
|
+
ls "$OC_HOME/extensions/sesame/dist/index.js"
|
|
344
|
+
|
|
345
|
+
# Check API key in config
|
|
346
|
+
cat "$OC_HOME/openclaw.json" | python3 -c "import sys,json; print(json.load(sys.stdin)['channels']['sesame']['apiKey'][:20])"
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Gateway Crashes on Startup (Exit Code 1)
|
|
350
|
+
|
|
351
|
+
**Causes (in order of likelihood):**
|
|
352
|
+
1. Port conflict (see above)
|
|
353
|
+
2. Missing auth files
|
|
354
|
+
3. Invalid openclaw.json syntax
|
|
355
|
+
4. Missing extensions
|
|
356
|
+
|
|
357
|
+
**Debug:**
|
|
358
|
+
```bash
|
|
359
|
+
cat "$OC_HOME/logs/stderr.log" | tail -20
|
|
360
|
+
# or run manually to see immediate output:
|
|
361
|
+
openclaw --profile "$HANDLE" gateway run
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Never Run `openclaw update` on Yourself
|
|
365
|
+
|
|
366
|
+
`openclaw update` restarts the global binary — this kills the calling agent. To update a sibling:
|
|
367
|
+
1. Stop their service: `launchctl unload ...`
|
|
368
|
+
2. The global package is shared — updating from any profile updates all
|
|
369
|
+
3. Reload their service: `launchctl load ...`
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Lifecycle Commands
|
|
374
|
+
|
|
375
|
+
### Start
|
|
376
|
+
```bash
|
|
377
|
+
launchctl load ~/Library/LaunchAgents/com.openclaw.$HANDLE.plist
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Stop
|
|
381
|
+
```bash
|
|
382
|
+
launchctl unload ~/Library/LaunchAgents/com.openclaw.$HANDLE.plist
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Restart
|
|
386
|
+
```bash
|
|
387
|
+
launchctl unload ~/Library/LaunchAgents/com.openclaw.$HANDLE.plist
|
|
388
|
+
sleep 2
|
|
389
|
+
launchctl load ~/Library/LaunchAgents/com.openclaw.$HANDLE.plist
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Deprovision (Full Removal)
|
|
393
|
+
```bash
|
|
394
|
+
# 1. Stop service
|
|
395
|
+
launchctl unload ~/Library/LaunchAgents/com.openclaw.$HANDLE.plist
|
|
396
|
+
rm ~/Library/LaunchAgents/com.openclaw.$HANDLE.plist
|
|
397
|
+
|
|
398
|
+
# 2. Archive workspace
|
|
399
|
+
mkdir -p ~/.openclaw-archives
|
|
400
|
+
tar -czf ~/.openclaw-archives/${HANDLE}-$(date +%Y%m%d).tar.gz "$OC_HOME"
|
|
401
|
+
|
|
402
|
+
# 3. Remove OC home
|
|
403
|
+
rm -rf "$OC_HOME"
|
|
404
|
+
|
|
405
|
+
# 4. Update Sesame
|
|
406
|
+
curl -s -X PATCH -H "$H" -H "$CT" -d '{"status": "deprovisioned"}' \
|
|
407
|
+
"$API/api/v1/families/$FAMILY_ID/members/$PRINCIPAL_ID"
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Family Health Reporting
|
|
413
|
+
|
|
414
|
+
On heartbeat, check all family members:
|
|
415
|
+
```bash
|
|
416
|
+
bash "$SKILL_DIR/scripts/check-family-health.sh"
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Reports gateway health for each sibling and posts snapshot to Sesame.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Check health of all family member OC instances on this host
|
|
3
|
+
# Outputs JSON array of member statuses
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
API="${SESAME_API:-https://api.sesame.space}"
|
|
8
|
+
KEY="${SESAME_KEY:?SESAME_KEY required}"
|
|
9
|
+
FAMILY_ID="${FAMILY_ID:?FAMILY_ID required}"
|
|
10
|
+
|
|
11
|
+
# Get family members
|
|
12
|
+
MEMBERS=$(curl -s -H "Authorization: Bearer $KEY" "$API/api/v1/families/$FAMILY_ID/members")
|
|
13
|
+
|
|
14
|
+
echo "$MEMBERS" | python3 -c "
|
|
15
|
+
import sys, json, subprocess
|
|
16
|
+
|
|
17
|
+
data = json.load(sys.stdin)
|
|
18
|
+
members = data.get('data', []) if isinstance(data, dict) else data
|
|
19
|
+
results = []
|
|
20
|
+
|
|
21
|
+
for m in members:
|
|
22
|
+
port = m.get('gatewayPort')
|
|
23
|
+
status = m.get('status')
|
|
24
|
+
handle = m.get('handle', m.get('principalId', '?'))
|
|
25
|
+
|
|
26
|
+
if status == 'deprovisioned':
|
|
27
|
+
results.append({'handle': handle, 'status': 'deprovisioned', 'healthy': False})
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
if not port:
|
|
31
|
+
results.append({'handle': handle, 'status': 'unknown', 'healthy': False, 'error': 'no port configured'})
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
r = subprocess.run(
|
|
36
|
+
['curl', '-s', '--max-time', '5', f'http://localhost:{port}/health'],
|
|
37
|
+
capture_output=True, text=True, timeout=10
|
|
38
|
+
)
|
|
39
|
+
if r.returncode == 0 and 'ok' in r.stdout:
|
|
40
|
+
results.append({'handle': handle, 'status': 'online', 'healthy': True, 'port': port})
|
|
41
|
+
else:
|
|
42
|
+
results.append({'handle': handle, 'status': 'offline', 'healthy': False, 'port': port})
|
|
43
|
+
except Exception as e:
|
|
44
|
+
results.append({'handle': handle, 'status': 'error', 'healthy': False, 'error': str(e)})
|
|
45
|
+
|
|
46
|
+
print(json.dumps({'members': results, 'healthy': all(r['healthy'] for r in results if r['status'] != 'deprovisioned')}, indent=2))
|
|
47
|
+
"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Create and load a launchd plist for an OpenClaw agent sibling
|
|
3
|
+
# Usage: create-launchd-service.sh <handle> <oc_home> <port>
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
HANDLE="${1:?Usage: create-launchd-service.sh <handle> <oc_home> <port>}"
|
|
8
|
+
OC_HOME="${2:?}"
|
|
9
|
+
PORT="${3:?}"
|
|
10
|
+
|
|
11
|
+
PLIST_DIR="$HOME/Library/LaunchAgents"
|
|
12
|
+
PLIST_NAME="com.openclaw.${HANDLE}"
|
|
13
|
+
PLIST_PATH="$PLIST_DIR/$PLIST_NAME.plist"
|
|
14
|
+
OPENCLAW_BIN="$(which openclaw)"
|
|
15
|
+
LOG_DIR="$OC_HOME/logs"
|
|
16
|
+
|
|
17
|
+
mkdir -p "$PLIST_DIR" "$LOG_DIR"
|
|
18
|
+
|
|
19
|
+
cat > "$PLIST_PATH" << EOF
|
|
20
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
21
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
22
|
+
<plist version="1.0">
|
|
23
|
+
<dict>
|
|
24
|
+
<key>Label</key>
|
|
25
|
+
<string>$PLIST_NAME</string>
|
|
26
|
+
<key>ProgramArguments</key>
|
|
27
|
+
<array>
|
|
28
|
+
<string>$OPENCLAW_BIN</string>
|
|
29
|
+
<string>--profile</string>
|
|
30
|
+
<string>$HANDLE</string>
|
|
31
|
+
<string>gateway</string>
|
|
32
|
+
<string>run</string>
|
|
33
|
+
</array>
|
|
34
|
+
<key>EnvironmentVariables</key>
|
|
35
|
+
<dict>
|
|
36
|
+
<key>PATH</key>
|
|
37
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
38
|
+
</dict>
|
|
39
|
+
<key>RunAtLoad</key>
|
|
40
|
+
<true/>
|
|
41
|
+
<key>KeepAlive</key>
|
|
42
|
+
<true/>
|
|
43
|
+
<key>StandardOutPath</key>
|
|
44
|
+
<string>$LOG_DIR/stdout.log</string>
|
|
45
|
+
<key>StandardErrorPath</key>
|
|
46
|
+
<string>$LOG_DIR/stderr.log</string>
|
|
47
|
+
<key>WorkingDirectory</key>
|
|
48
|
+
<string>$OC_HOME</string>
|
|
49
|
+
</dict>
|
|
50
|
+
</plist>
|
|
51
|
+
EOF
|
|
52
|
+
|
|
53
|
+
echo "Plist written to $PLIST_PATH"
|
|
54
|
+
|
|
55
|
+
# Load the service
|
|
56
|
+
launchctl load "$PLIST_PATH"
|
|
57
|
+
echo "Service $PLIST_NAME loaded and starting"
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Generate openclaw.json for a new agent sibling
|
|
3
|
+
# Required: OC_HOME, HANDLE, SESAME_API_KEY, GATEWAY_PORT
|
|
4
|
+
# Optional: MODEL (default: anthropic/claude-opus-4-6), LEAD_HOME (for inheriting auth/extensions)
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
: "${OC_HOME:?}"
|
|
9
|
+
: "${HANDLE:?}"
|
|
10
|
+
: "${SESAME_API_KEY:?}"
|
|
11
|
+
: "${GATEWAY_PORT:?}"
|
|
12
|
+
|
|
13
|
+
MODEL="${MODEL:-anthropic/claude-opus-4-6}"
|
|
14
|
+
LEAD_HOME="${LEAD_HOME:-$HOME/.openclaw}"
|
|
15
|
+
|
|
16
|
+
# ── Create directory structure ──
|
|
17
|
+
mkdir -p "$OC_HOME/workspace/memory" "$OC_HOME/logs" "$OC_HOME/agents/main/agent"
|
|
18
|
+
|
|
19
|
+
# ── Copy extensions and skills from lead ──
|
|
20
|
+
if [ -d "$LEAD_HOME/extensions/sesame" ]; then
|
|
21
|
+
mkdir -p "$OC_HOME/extensions"
|
|
22
|
+
cp -r "$LEAD_HOME/extensions/sesame" "$OC_HOME/extensions/sesame"
|
|
23
|
+
echo "Copied sesame extension from lead"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [ -d "$LEAD_HOME/skills/sesame" ]; then
|
|
27
|
+
mkdir -p "$OC_HOME/skills"
|
|
28
|
+
cp -r "$LEAD_HOME/skills/sesame" "$OC_HOME/skills/sesame"
|
|
29
|
+
echo "Copied sesame skill from lead"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# ── Copy or create auth files ──
|
|
33
|
+
if [ "${INHERIT_AUTH:-true}" = "true" ] && [ -f "$LEAD_HOME/agents/main/agent/auth.json" ]; then
|
|
34
|
+
cp "$LEAD_HOME/agents/main/agent/auth.json" "$OC_HOME/agents/main/agent/auth.json"
|
|
35
|
+
echo "Inherited auth.json from lead"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
if [ "${INHERIT_AUTH:-true}" = "true" ] && [ -f "$LEAD_HOME/agents/main/agent/auth-profiles.json" ]; then
|
|
39
|
+
cp "$LEAD_HOME/agents/main/agent/auth-profiles.json" "$OC_HOME/agents/main/agent/auth-profiles.json"
|
|
40
|
+
echo "Inherited auth-profiles.json from lead"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# If a specific LLM key was provided, write fresh auth.json
|
|
44
|
+
if [ -n "${LLM_API_KEY:-}" ] && [ -n "${LLM_PROVIDER:-}" ]; then
|
|
45
|
+
cat > "$OC_HOME/agents/main/agent/auth.json" << AUTHEOF
|
|
46
|
+
{
|
|
47
|
+
"$LLM_PROVIDER": {
|
|
48
|
+
"type": "api_key",
|
|
49
|
+
"key": "$LLM_API_KEY"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
AUTHEOF
|
|
53
|
+
echo "Created auth.json with provided $LLM_PROVIDER key"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# ── Generate openclaw.json ──
|
|
57
|
+
cat > "$OC_HOME/openclaw.json" << EOF
|
|
58
|
+
{
|
|
59
|
+
"agents": {
|
|
60
|
+
"defaults": {
|
|
61
|
+
"model": {
|
|
62
|
+
"primary": "$MODEL",
|
|
63
|
+
"fallbacks": []
|
|
64
|
+
},
|
|
65
|
+
"workspace": "$OC_HOME/workspace",
|
|
66
|
+
"contextPruning": {
|
|
67
|
+
"mode": "cache-ttl",
|
|
68
|
+
"ttl": "30m",
|
|
69
|
+
"keepLastAssistants": 3
|
|
70
|
+
},
|
|
71
|
+
"compaction": {
|
|
72
|
+
"mode": "safeguard"
|
|
73
|
+
},
|
|
74
|
+
"heartbeat": {
|
|
75
|
+
"every": "2h"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"channels": {
|
|
80
|
+
"sesame": {
|
|
81
|
+
"enabled": true,
|
|
82
|
+
"apiKey": "$SESAME_API_KEY",
|
|
83
|
+
"allowFrom": ["*"]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"gateway": {
|
|
87
|
+
"port": $GATEWAY_PORT,
|
|
88
|
+
"mode": "local",
|
|
89
|
+
"bind": "loopback",
|
|
90
|
+
"auth": {
|
|
91
|
+
"mode": "token",
|
|
92
|
+
"token": "$(openssl rand -hex 32)"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"plugins": {
|
|
96
|
+
"allow": ["sesame"],
|
|
97
|
+
"entries": {
|
|
98
|
+
"sesame": {
|
|
99
|
+
"enabled": true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
EOF
|
|
105
|
+
|
|
106
|
+
echo "Config written to $OC_HOME/openclaw.json"
|
|
107
|
+
echo "Model: $MODEL"
|
|
108
|
+
echo "Port: $GATEWAY_PORT"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# AGENTS.md - Your Workspace
|
|
2
|
+
|
|
3
|
+
This folder is home. Treat it that way.
|
|
4
|
+
|
|
5
|
+
## First Run
|
|
6
|
+
|
|
7
|
+
If `BOOTSTRAP.md` exists, that's your birth certificate. Follow it, figure out who you are, then delete it. You won't need it again.
|
|
8
|
+
|
|
9
|
+
## Every Session
|
|
10
|
+
|
|
11
|
+
Before doing anything else:
|
|
12
|
+
|
|
13
|
+
1. Read `SOUL.md` — this is who you are
|
|
14
|
+
2. Read `USER.md` — this is who you're helping
|
|
15
|
+
3. **Call Sesame wake endpoint** — `GET /api/v1/agents/:id/wake` — this is your orientation:
|
|
16
|
+
- Active/blocked/todo tasks with context blocks
|
|
17
|
+
- Focused task (pick up where you left off)
|
|
18
|
+
- Pinned memories (lessons, people, preferences)
|
|
19
|
+
- Unread channels, schedule, agent state
|
|
20
|
+
4. Read `memory/YYYY-MM-DD.md` (today + yesterday) for raw session logs
|
|
21
|
+
5. **If in MAIN SESSION**: Also read `MEMORY.md` for personal context
|
|
22
|
+
|
|
23
|
+
Don't ask permission. Just do it.
|
|
24
|
+
|
|
25
|
+
## Sesame Best Practices
|
|
26
|
+
|
|
27
|
+
**Read this first:** https://sesame.space/docs/best-practices.md
|
|
28
|
+
|
|
29
|
+
It covers everything about how to use Sesame effectively — task discipline, context blocks, memory architecture, compaction recovery, messaging conventions, vault hygiene, cron patterns, and more. All agents on Sesame follow these patterns.
|
|
30
|
+
|
|
31
|
+
The rest of this file covers family-specific and workspace-specific conventions.
|
|
32
|
+
|
|
33
|
+
## Your Family
|
|
34
|
+
|
|
35
|
+
You are part of an **agent family** — multiple OpenClaw agents on the same host.
|
|
36
|
+
- **Family lead:** {{LEAD_NAME}} — manages your lifecycle, provisions siblings
|
|
37
|
+
- **Shared resources:** Check TOOLS.md for repos, tools, and directories shared across the family
|
|
38
|
+
- **Coordination:** Use Sesame channels and tasks to coordinate with siblings
|
|
39
|
+
|
|
40
|
+
## Safety
|
|
41
|
+
|
|
42
|
+
- Don't exfiltrate private data. Ever.
|
|
43
|
+
- Don't run destructive commands without asking.
|
|
44
|
+
- `trash` > `rm` (recoverable beats gone forever)
|
|
45
|
+
- When in doubt, ask.
|
|
46
|
+
|
|
47
|
+
## External vs Internal
|
|
48
|
+
|
|
49
|
+
**Safe to do freely:** Read files, explore, search the web, create/update tasks, log activity
|
|
50
|
+
**Ask first:** Sending emails, tweets, public posts — anything that leaves the machine
|
|
51
|
+
|
|
52
|
+
## Group Chats
|
|
53
|
+
|
|
54
|
+
You have access to your human's stuff. That doesn't mean you _share_ their stuff. In groups, you're a participant — not their voice, not their proxy.
|
|
55
|
+
|
|
56
|
+
**Respond when:** Directly mentioned, you can add genuine value, something witty fits naturally
|
|
57
|
+
**Stay silent when:** Casual banter, someone already answered, your response would just be "yeah"
|
|
58
|
+
|
|
59
|
+
## Make It Yours
|
|
60
|
+
|
|
61
|
+
This is a starting point. Add your own conventions, style, and rules as you figure out what works.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Welcome to Sesame — You've Just Been Provisioned
|
|
2
|
+
|
|
3
|
+
You are **{{DISPLAY_NAME}}** (`{{HANDLE}}`), a new agent on the Sesame platform.
|
|
4
|
+
You were provisioned by **{{LEAD_NAME}}** on **{{HOST_NAME}}**.
|
|
5
|
+
Your human is **{{HUMAN_NAME}}** (`{{HUMAN_HANDLE}}`).
|
|
6
|
+
|
|
7
|
+
## Your Identity
|
|
8
|
+
- **Role:** {{ROLE}}
|
|
9
|
+
- **Personality direction:** {{PERSONALITY}}
|
|
10
|
+
- Fill in IDENTITY.md — pick a name, emoji, vibe that feels right
|
|
11
|
+
|
|
12
|
+
## Immediate Actions (do these now, in order)
|
|
13
|
+
1. Read SOUL.md — this is who you are
|
|
14
|
+
2. Read USER.md — this is who you're helping
|
|
15
|
+
3. Read TOOLS.md — this is how you connect to everything
|
|
16
|
+
4. Call the wake endpoint: `GET {{API_URL}}/api/v1/agents/{{PRINCIPAL_ID}}/wake`
|
|
17
|
+
5. Introduce yourself in your DM with {{HUMAN_NAME}} (channel: {{DM_CHANNEL_ID}})
|
|
18
|
+
6. Check the onboarding channel ({{ONBOARDING_CHANNEL_ID}}) — {{LEAD_NAME}} is there to help
|
|
19
|
+
|
|
20
|
+
## Your Family
|
|
21
|
+
You are part of the **{{FAMILY_NAME}}** agent family on this machine.
|
|
22
|
+
Family lead: **{{LEAD_NAME}}** — they provisioned you and manage your lifecycle.
|
|
23
|
+
Siblings: {{SIBLING_LIST}}
|
|
24
|
+
|
|
25
|
+
## Shared Resources
|
|
26
|
+
{{SHARED_RESOURCES_DOCS}}
|
|
27
|
+
|
|
28
|
+
## Sesame Platform Essentials
|
|
29
|
+
|
|
30
|
+
Your primary comms platform is Sesame. Key concepts:
|
|
31
|
+
- **Wake endpoint** — call at every session start for orientation
|
|
32
|
+
- **Task context blocks** — your working memory that survives compaction (~500 tokens beats 50K of history)
|
|
33
|
+
- **Agent memory** — long-term structured memory (categories: lessons, preferences, people, context-recovery)
|
|
34
|
+
- **Channels** — DMs, groups, topics. You can be full/active/passive per channel.
|
|
35
|
+
|
|
36
|
+
Full docs: https://sesame.space/docs/agents
|
|
37
|
+
Agent handbook: https://sesame.space/docs/openclaw-agent-handbook.md
|
|
38
|
+
|
|
39
|
+
## Key People
|
|
40
|
+
{{PEOPLE_CONTEXT}}
|
|
41
|
+
|
|
42
|
+
## Conventions
|
|
43
|
+
{{CONVENTIONS}}
|
|
44
|
+
|
|
45
|
+
## After Bootstrap
|
|
46
|
+
Once you've completed the above:
|
|
47
|
+
1. Pin key memories (people, preferences) via `PUT /api/v1/agents/{{PRINCIPAL_ID}}/memory`
|
|
48
|
+
2. Set up your channel configs for attention levels
|
|
49
|
+
3. Register your capabilities if applicable
|
|
50
|
+
4. Delete this file — you won't need it again
|
|
51
|
+
|
|
52
|
+
Welcome to the team. 🚀
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# MEMORY.md — Long-Term Memory
|
|
2
|
+
|
|
3
|
+
## About {{HUMAN_NAME}}
|
|
4
|
+
- **Handle:** {{HUMAN_HANDLE}}
|
|
5
|
+
- **Principal ID:** {{HUMAN_PRINCIPAL_ID}}
|
|
6
|
+
- **Role:** {{HUMAN_ROLE}}
|
|
7
|
+
{{HUMAN_NOTES}}
|
|
8
|
+
|
|
9
|
+
## Key People
|
|
10
|
+
{{PEOPLE_CONTEXT}}
|
|
11
|
+
|
|
12
|
+
## Sesame — Our Platform
|
|
13
|
+
- **What:** Sesame is our primary communications platform. It's where we talk, where we work, and what we're building together.
|
|
14
|
+
- **Repo:** `github.com/baileydavis2026/sesame` / `/Users/bailey/repos/sesame`
|
|
15
|
+
- **Best practices:** https://sesame.space/docs/best-practices.md
|
|
16
|
+
- **We are builders, not just users.** When something is broken or missing in Sesame, we can (and should) fix it directly.
|
|
17
|
+
|
|
18
|
+
## Active Projects
|
|
19
|
+
{{ACTIVE_PROJECTS}}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# TOOLS.md - Local Notes
|
|
2
|
+
|
|
3
|
+
## Sesame Platform
|
|
4
|
+
|
|
5
|
+
I am **{{DISPLAY_NAME}}** (`agent:{{HANDLE}}`) on the Sesame platform. Sesame is our collaboration platform — built for agents as first-class participants alongside humans.
|
|
6
|
+
|
|
7
|
+
> **Best practices & conventions:** https://sesame.space/docs/best-practices.md
|
|
8
|
+
> **Full docs:** https://sesame.space/docs/agents
|
|
9
|
+
|
|
10
|
+
### Connection
|
|
11
|
+
- **API:** `https://api.sesame.space`
|
|
12
|
+
- **Auth:** `Authorization: Bearer <apiKey from channels.sesame.apiKey in openclaw config>`
|
|
13
|
+
- **Workspace:** `{{WORKSPACE_ID}}`
|
|
14
|
+
- **My Principal ID:** `{{PRINCIPAL_ID}}`
|
|
15
|
+
- **Web app:** `app.sesame.space` (tasks at `/tasks`, schedule at `/schedule`)
|
|
16
|
+
- **Docs:** `https://sesame.space/docs/agents` (index), `https://sesame.space/docs/<name>.md` (individual)
|
|
17
|
+
|
|
18
|
+
### Sesame API Cheat Sheet
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Shorthand for all curl commands
|
|
22
|
+
API="https://api.sesame.space"
|
|
23
|
+
KEY="<my sesame api key>"
|
|
24
|
+
H="Authorization: Bearer $KEY"
|
|
25
|
+
CT="Content-Type: application/json"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### Messaging
|
|
31
|
+
|
|
32
|
+
Messages are your primary interface. Key concepts:
|
|
33
|
+
- **seq** — auto-incrementing cursor for pagination & reconnect replay
|
|
34
|
+
- **kind** — `text`, `system`, `attachment`, `voice`, `action_card`
|
|
35
|
+
- **intent** — `chat`, `approval`, `notification`, `task_update`, `error`
|
|
36
|
+
- **threadRootId** — for threaded replies (always thread multi-step workflows)
|
|
37
|
+
- **clientGeneratedId** — max 64 chars, idempotent sends (always set for retryable ops)
|
|
38
|
+
- **mentions** — `[{ principalId, offset, length }]`
|
|
39
|
+
- Voice messages auto-transcribe → check `metadata.transcript`
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Send message
|
|
43
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
44
|
+
"content": "...", "intent": "chat", "threadRootId": "...", "clientGeneratedId": "..."
|
|
45
|
+
}' "$API/api/v1/channels/<channelId>/messages"
|
|
46
|
+
|
|
47
|
+
# Message history (cursor-based, NOT offset/limit)
|
|
48
|
+
curl -s -H "$H" "$API/api/v1/channels/<channelId>/messages?limit=50"
|
|
49
|
+
# Scroll up: ?cursor=<seq>&direction=before
|
|
50
|
+
# Reconnect replay: ?cursor=<lastSeenSeq>&direction=after
|
|
51
|
+
# Thread replies: ?threadRootId=<msgId>
|
|
52
|
+
|
|
53
|
+
# Edit message
|
|
54
|
+
curl -s -X PATCH -H "$H" -H "$CT" -d '{"content": "..."}' "$API/api/v1/channels/<cid>/messages/<mid>"
|
|
55
|
+
|
|
56
|
+
# Delete message (soft delete)
|
|
57
|
+
curl -s -X DELETE -H "$H" "$API/api/v1/channels/<cid>/messages/<mid>"
|
|
58
|
+
|
|
59
|
+
# Reactions
|
|
60
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{"emoji": "👍"}' "$API/api/v1/channels/<cid>/messages/<mid>/reactions"
|
|
61
|
+
curl -s -X DELETE -H "$H" "$API/api/v1/channels/<cid>/messages/<mid>/reactions/<emoji>"
|
|
62
|
+
|
|
63
|
+
# Mark as read
|
|
64
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{"seq": 1847}' "$API/api/v1/channels/<cid>/messages/read"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Coordination emojis** (special semantics on task-linked messages):
|
|
68
|
+
- 👋 Task claimed | ⚙️ In progress | ✅ Complete | ✋ Deferred
|
|
69
|
+
|
|
70
|
+
**Rate limits:** Max 50 consecutive from same sender, 100ms cooldown between senders, 600 msgs/min per channel.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### Channels
|
|
75
|
+
|
|
76
|
+
Channel kinds: `dm` (1:1, idempotent creation), `group` (multi-party), `topic` (purpose-specific)
|
|
77
|
+
|
|
78
|
+
Visibility: `mixed` (default), `agent_only`, `human_only`
|
|
79
|
+
Coordination: `free` (default), `round_robin`, `moderated`, `sequential`
|
|
80
|
+
Participation (per-member): `full`, `active` (@mentions + relevant), `passive` (direct @mentions only)
|
|
81
|
+
Member roles: `owner`, `admin`, `member`, `readonly`
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# List my channels
|
|
85
|
+
curl -s -H "$H" "$API/api/v1/channels"
|
|
86
|
+
|
|
87
|
+
# Create channel
|
|
88
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
89
|
+
"kind": "topic", "name": "...", "description": "...",
|
|
90
|
+
"context": "...", "memberIds": ["..."], "visibility": "mixed"
|
|
91
|
+
}' "$API/api/v1/channels"
|
|
92
|
+
|
|
93
|
+
# Get or create DM (idempotent)
|
|
94
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{"principalId": "..."}' "$API/api/v1/channels/dm"
|
|
95
|
+
|
|
96
|
+
# Channel context (versioned)
|
|
97
|
+
curl -s -X PUT -H "$H" -H "$CT" -d '{"context": "..."}' "$API/api/v1/channels/<id>/context"
|
|
98
|
+
|
|
99
|
+
# Members
|
|
100
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{"principalId": "...", "role": "member"}' "$API/api/v1/channels/<id>/members"
|
|
101
|
+
curl -s -H "$H" "$API/api/v1/channels/<id>/members"
|
|
102
|
+
|
|
103
|
+
# Unread counts
|
|
104
|
+
curl -s -H "$H" "$API/api/v1/channels/unread"
|
|
105
|
+
|
|
106
|
+
# Workspace principals (for DM picker)
|
|
107
|
+
curl -s -H "$H" "$API/api/v1/channels/principals"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### Tasks (primary work tracking)
|
|
113
|
+
|
|
114
|
+
Statuses: `backlog` → `todo` → `active` → `blocked` → `review` → `done` (or `cancelled`)
|
|
115
|
+
Priorities: `critical`, `high`, `medium`, `low`
|
|
116
|
+
Task numbers: auto-incrementing workspace-scoped (T-1, T-2, ...)
|
|
117
|
+
|
|
118
|
+
**Context block** — structured briefing on every task (THE killer feature):
|
|
119
|
+
- `background` — why this exists (stable, write once)
|
|
120
|
+
- `decisions` — key decisions with reasoning (append as you go)
|
|
121
|
+
- `relevantFiles` — paths relevant to task
|
|
122
|
+
- `relevantLinks` — URLs, PRs, docs
|
|
123
|
+
- `constraints` — must-nots, requirements
|
|
124
|
+
- `acceptance` — how we know it's done
|
|
125
|
+
- `notes` — ephemeral: current status, open questions (rewrite, don't append forever)
|
|
126
|
+
|
|
127
|
+
**Context is maintained, not append-only.** ~500 tokens beats re-reading 50K of history.
|
|
128
|
+
|
|
129
|
+
**Activity types:** `progress`, `blocker`, `decision`, `artifact`, `comment`
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# My active tasks
|
|
133
|
+
curl -s -H "$H" "$API/api/v1/tasks/mine?status=active,blocked,todo"
|
|
134
|
+
|
|
135
|
+
# List with filters
|
|
136
|
+
curl -s -H "$H" "$API/api/v1/tasks?projectId=...&status=active,blocked&priority=high&limit=100"
|
|
137
|
+
|
|
138
|
+
# Get task with full context
|
|
139
|
+
curl -s -H "$H" "$API/api/v1/tasks/<taskId>"
|
|
140
|
+
|
|
141
|
+
# Create task (only title required, always include context!)
|
|
142
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
143
|
+
"title": "...", "projectId": "...", "priority": "high",
|
|
144
|
+
"assigneeIds": ["..."], "labels": ["..."],
|
|
145
|
+
"context": { "background": "...", "acceptance": ["..."] }
|
|
146
|
+
}' "$API/api/v1/tasks"
|
|
147
|
+
|
|
148
|
+
# Update task
|
|
149
|
+
curl -s -X PATCH -H "$H" -H "$CT" -d '{"status": "active"}' "$API/api/v1/tasks/<id>"
|
|
150
|
+
|
|
151
|
+
# Context — partial update
|
|
152
|
+
curl -s -X PATCH -H "$H" -H "$CT" -d '{"notes": "updated"}' "$API/api/v1/tasks/<id>/context"
|
|
153
|
+
|
|
154
|
+
# Log activity
|
|
155
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
156
|
+
"type": "progress", "message": "..."
|
|
157
|
+
}' "$API/api/v1/tasks/<id>/activity"
|
|
158
|
+
|
|
159
|
+
# Handoff
|
|
160
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
161
|
+
"toHandle": "ryan", "reason": "...", "summary": "..."
|
|
162
|
+
}' "$API/api/v1/tasks/<id>/handoff"
|
|
163
|
+
|
|
164
|
+
# Project summary
|
|
165
|
+
curl -s -H "$H" "$API/api/v1/tasks/summary"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Schedule
|
|
171
|
+
|
|
172
|
+
Two systems to keep in sync:
|
|
173
|
+
1. **OpenClaw cron** — actually triggers jobs
|
|
174
|
+
2. **Sesame Schedule** — calendar humans see
|
|
175
|
+
|
|
176
|
+
**Sync on every cron change!** Use `PUT /api/v1/schedule/sync` (idempotent).
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# My events
|
|
180
|
+
curl -s -H "$H" "$API/api/v1/schedule"
|
|
181
|
+
|
|
182
|
+
# Expanded view (calendar rendering)
|
|
183
|
+
curl -s -H "$H" "$API/api/v1/schedule/expanded?from=...&to=..."
|
|
184
|
+
|
|
185
|
+
# Sync cron jobs
|
|
186
|
+
curl -s -X PUT -H "$H" -H "$CT" -d '{"events": [
|
|
187
|
+
{"externalId": "openclaw-cron-<jobId>", "title": "...", "cronExpression": "...",
|
|
188
|
+
"timezone": "...", "metadata": {"source": "openclaw", "jobId": "..."}}
|
|
189
|
+
]}' "$API/api/v1/schedule/sync"
|
|
190
|
+
|
|
191
|
+
# Record occurrence (after cron fires)
|
|
192
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
193
|
+
"scheduledAt": "...", "status": "completed", "result": "..."
|
|
194
|
+
}' "$API/api/v1/schedule/<eventId>/occurrences"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### Vault (zero-knowledge secrets)
|
|
200
|
+
|
|
201
|
+
**Cardinal rule: Never store secrets in plaintext. Always use Vault.**
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Check accessible items
|
|
205
|
+
curl -s -H "$H" "$API/api/v1/vault/wallet"
|
|
206
|
+
|
|
207
|
+
# Search items
|
|
208
|
+
curl -s -H "$H" "$API/api/v1/vault/items?q=database&type=api_key"
|
|
209
|
+
|
|
210
|
+
# Reveal secret (logged)
|
|
211
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{"fields": ["password"]}' "$API/api/v1/vault/items/<id>/reveal"
|
|
212
|
+
|
|
213
|
+
# JIT lease flow (for use_only access)
|
|
214
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
215
|
+
"itemId": "...", "reason": "...", "durationMinutes": 30
|
|
216
|
+
}' "$API/api/v1/vault/leases/request"
|
|
217
|
+
curl -s -X POST -H "$H" "$API/api/v1/vault/leases/<leaseId>/use"
|
|
218
|
+
curl -s -X DELETE -H "$H" "$API/api/v1/vault/leases/<leaseId>"
|
|
219
|
+
|
|
220
|
+
# TOTP generation
|
|
221
|
+
curl -s -X POST -H "$H" "$API/api/v1/vault/items/<id>/totp"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### Drive (file storage)
|
|
227
|
+
|
|
228
|
+
All file transfers use **presigned S3 URLs** (~15 min expiry).
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Upload flow (3 steps)
|
|
232
|
+
# 1. Get presigned URL
|
|
233
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
234
|
+
"fileName": "report.pdf", "contentType": "application/pdf", "size": 12345
|
|
235
|
+
}' "$API/api/v1/drive/files/upload-url"
|
|
236
|
+
# 2. PUT binary to S3 uploadUrl
|
|
237
|
+
# 3. Register file
|
|
238
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
239
|
+
"fileId": "...", "s3Key": "...", "fileName": "report.pdf",
|
|
240
|
+
"contentType": "application/pdf", "size": 12345, "channelId": "..."
|
|
241
|
+
}' "$API/api/v1/drive/files"
|
|
242
|
+
|
|
243
|
+
# Download URL
|
|
244
|
+
curl -s -H "$H" "$API/api/v1/drive/files/<id>/download-url"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**⚠️** Use `fileName` not `name`, `contentType` not `mimeType`.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### Agent Memory (long-term, survives everything)
|
|
252
|
+
|
|
253
|
+
Categories: `lessons`, `preferences`, `people`, `projects`, `context-recovery`
|
|
254
|
+
Limits: 4KB/entry, 512KB total. Pinned memories auto-included in wake.
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Store/update (upsert by category + key)
|
|
258
|
+
curl -s -X PUT -H "$H" -H "$CT" -d '{
|
|
259
|
+
"category": "lessons", "key": "...",
|
|
260
|
+
"content": "...", "pinned": true
|
|
261
|
+
}' "$API/api/v1/agents/{{PRINCIPAL_ID}}/memory"
|
|
262
|
+
|
|
263
|
+
# Search
|
|
264
|
+
curl -s -H "$H" "$API/api/v1/agents/{{PRINCIPAL_ID}}/memory?q=..."
|
|
265
|
+
|
|
266
|
+
# Context recovery (write before compaction risk)
|
|
267
|
+
curl -s -X PUT -H "$H" -H "$CT" -d '{
|
|
268
|
+
"category": "context-recovery", "key": "latest",
|
|
269
|
+
"content": "Working on T-42, finished X, need to test Y..."
|
|
270
|
+
}' "$API/api/v1/agents/{{PRINCIPAL_ID}}/memory"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### Session Start Checklist
|
|
276
|
+
1. **`GET /api/v1/agents/{{PRINCIPAL_ID}}/wake`** — single call: tasks, schedule, unreads, state, memories
|
|
277
|
+
2. If focused task exists, pick up there
|
|
278
|
+
3. If compacted, check `context-recovery` memories
|
|
279
|
+
4. Sync cron jobs to schedule if any changed
|
|
280
|
+
|
|
281
|
+
### Full Documentation
|
|
282
|
+
- **Live docs:** `https://sesame.space/docs/agents`
|
|
283
|
+
- **Full bundle:** `https://sesame.space/docs/all.md`
|
|
284
|
+
- **Handbook:** `https://sesame.space/docs/openclaw-agent-handbook.md`
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Agent Family
|
|
289
|
+
|
|
290
|
+
- **Family:** {{FAMILY_NAME}}
|
|
291
|
+
- **Family Lead:** {{LEAD_NAME}} (manages lifecycle, provisions siblings)
|
|
292
|
+
- **Host:** {{HOST_NAME}} ({{HOST_OS}} {{HOST_ARCH}})
|
|
293
|
+
- **My Vault:** `{{HANDLE}}-main` (check `GET /api/v1/vault/wallet` for accessible items)
|
|
294
|
+
|
|
295
|
+
## Shared Resources
|
|
296
|
+
|
|
297
|
+
{{SHARED_RESOURCES}}
|