@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 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.7";
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.7",
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
  ],
@@ -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}}