@sesamespace/sesame 0.2.6 → 0.2.8
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 +33 -7
- package/package.json +5 -2
- package/postinstall.mjs +59 -0
- package/skills/agent-provisioner/SKILL.md +155 -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.8";
|
|
17
17
|
/** Standard headers for Sesame API calls */
|
|
18
18
|
function sesameHeaders(apiKey, json = true) {
|
|
19
19
|
const h = {
|
|
@@ -46,6 +46,18 @@ function guessContentType(fileName) {
|
|
|
46
46
|
};
|
|
47
47
|
return map[ext] ?? "application/octet-stream";
|
|
48
48
|
}
|
|
49
|
+
/** Detect gateway-generated error messages that shouldn't appear as normal chat */
|
|
50
|
+
const ERROR_PATTERNS = [
|
|
51
|
+
/^The AI service is temporarily overloaded/,
|
|
52
|
+
/^The AI service is temporarily unavailable/,
|
|
53
|
+
/^The AI service returned an error/,
|
|
54
|
+
/^⚠️.*error/i,
|
|
55
|
+
/^Error:/i,
|
|
56
|
+
];
|
|
57
|
+
function isGatewayErrorMessage(text) {
|
|
58
|
+
const trimmed = text.trim();
|
|
59
|
+
return ERROR_PATTERNS.some((p) => p.test(trimmed));
|
|
60
|
+
}
|
|
49
61
|
const sesameChannelPlugin = {
|
|
50
62
|
id: "sesame",
|
|
51
63
|
meta: {
|
|
@@ -182,10 +194,12 @@ const sesameChannelPlugin = {
|
|
|
182
194
|
chatId: channelId,
|
|
183
195
|
};
|
|
184
196
|
}
|
|
197
|
+
// Detect error messages from the gateway and tag them appropriately
|
|
198
|
+
const intent = isGatewayErrorMessage(text) ? "error" : "chat";
|
|
185
199
|
const response = await fetch(`${account.apiUrl}/api/v1/channels/${channelId}/messages`, {
|
|
186
200
|
method: "POST",
|
|
187
201
|
headers: sesameHeaders(account.apiKey),
|
|
188
|
-
body: JSON.stringify({ content: text, kind: "text", intent
|
|
202
|
+
body: JSON.stringify({ content: text, kind: "text", intent }),
|
|
189
203
|
});
|
|
190
204
|
if (!response.ok) {
|
|
191
205
|
const error = (await response.json().catch(() => ({})));
|
|
@@ -753,7 +767,11 @@ async function handleMessage(message, account, state, ctx) {
|
|
|
753
767
|
ctx: inboundCtx,
|
|
754
768
|
});
|
|
755
769
|
// ── Streaming reply: send initial message, then edit in place ──
|
|
756
|
-
|
|
770
|
+
// Only stream for human senders in DM channels — agents get buffer mode
|
|
771
|
+
const configStreamMode = cfg.channels?.sesame?.streamMode ?? "buffer";
|
|
772
|
+
const senderKind = meta.senderKind ?? message.sender?.kind;
|
|
773
|
+
const isHumanDm = channelKind === "dm" && senderKind === "human";
|
|
774
|
+
const sesameStreamMode = isHumanDm ? configStreamMode : "buffer";
|
|
757
775
|
const replyBuffer = [];
|
|
758
776
|
let streamMessageId = null;
|
|
759
777
|
let streamSending = false; // lock to prevent concurrent first-message sends
|
|
@@ -779,10 +797,11 @@ async function handleMessage(message, account, state, ctx) {
|
|
|
779
797
|
log.error?.(`[sesame] Circuit breaker: suppressing outbound message`);
|
|
780
798
|
return null;
|
|
781
799
|
}
|
|
800
|
+
const intent = isGatewayErrorMessage(text) ? "error" : "chat";
|
|
782
801
|
const res = await fetch(`${account.apiUrl}/api/v1/channels/${channelId}/messages`, {
|
|
783
802
|
method: "POST",
|
|
784
803
|
headers: sesameHeaders(account.apiKey),
|
|
785
|
-
body: JSON.stringify({ content: text, kind: "text", intent
|
|
804
|
+
body: JSON.stringify({ content: text, kind: "text", intent }),
|
|
786
805
|
});
|
|
787
806
|
if (!res.ok) {
|
|
788
807
|
const err = await res.text().catch(() => "");
|
|
@@ -839,9 +858,16 @@ async function handleMessage(message, account, state, ctx) {
|
|
|
839
858
|
log.info?.(`[sesame] Final stream edit (${fullReply.length} chars), marked as delivered`);
|
|
840
859
|
}
|
|
841
860
|
else {
|
|
842
|
-
// No streaming happened —
|
|
843
|
-
// (
|
|
844
|
-
|
|
861
|
+
// No streaming happened — send the buffered reply directly
|
|
862
|
+
// (outbound.sendText is not reliably called by OC core in all configurations)
|
|
863
|
+
const sentId = await sendNewMessage(fullReply);
|
|
864
|
+
if (sentId) {
|
|
865
|
+
streamDelivered.set(channelId, { messageId: sentId, text: fullReply });
|
|
866
|
+
log.info?.(`[sesame] Buffer mode: sent directly (${fullReply.length} chars), msgId=${sentId}`);
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
log.error?.(`[sesame] Buffer mode: direct send failed (${fullReply.length} chars)`);
|
|
870
|
+
}
|
|
845
871
|
}
|
|
846
872
|
};
|
|
847
873
|
const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sesamespace/sesame",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
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,155 @@
|
|
|
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
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- A provisioning task is assigned to you (title starts with "Provision agent:")
|
|
13
|
+
- A lifecycle command arrives (start/stop/restart/deprovision) for a family member
|
|
14
|
+
- You need to register this host as a family or update shared resources
|
|
15
|
+
|
|
16
|
+
## Provisioning Flow
|
|
17
|
+
|
|
18
|
+
When you receive a provisioning task, extract the details from the task context and the onboarding channel messages. You need:
|
|
19
|
+
|
|
20
|
+
- **handle** — the new agent's Sesame handle
|
|
21
|
+
- **principalId** — their Sesame principal ID
|
|
22
|
+
- **apiKey** — their Sesame API key (from the provision response, shared via secure channel)
|
|
23
|
+
- **llmApiKey** — their LLM provider API key
|
|
24
|
+
- **llmProvider** — anthropic, openrouter, or openai
|
|
25
|
+
|
|
26
|
+
### Step 1: Create OC Home Directory
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
OC_HOME="$HOME/.openclaw-<handle>"
|
|
30
|
+
mkdir -p "$OC_HOME/workspace" "$OC_HOME/workspace/memory"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Step 2: Generate openclaw.json
|
|
34
|
+
|
|
35
|
+
Run the script: `scripts/generate-config.sh`
|
|
36
|
+
|
|
37
|
+
Required env vars:
|
|
38
|
+
- `OC_HOME` — home directory for new agent
|
|
39
|
+
- `HANDLE` — agent handle
|
|
40
|
+
- `SESAME_API_KEY` — Sesame API key
|
|
41
|
+
- `LLM_PROVIDER` — anthropic|openrouter|openai
|
|
42
|
+
- `LLM_API_KEY` — LLM API key
|
|
43
|
+
- `GATEWAY_PORT` — unique port (check existing family members, use next available from 18790+)
|
|
44
|
+
- `MODEL` — (optional) model override, defaults to provider default
|
|
45
|
+
|
|
46
|
+
### Step 3: Scaffold Workspace
|
|
47
|
+
|
|
48
|
+
Write these files into `$OC_HOME/workspace/`:
|
|
49
|
+
|
|
50
|
+
- **AGENTS.md** — Use the template in `scripts/templates/AGENTS.md`, customized with family info
|
|
51
|
+
- **SOUL.md** — Generate based on personality seed. If none provided, write a good default that emphasizes competence, helpfulness, and having opinions.
|
|
52
|
+
- **TOOLS.md** — Use `scripts/templates/TOOLS.md` as base, fill in Sesame API details and shared resource docs
|
|
53
|
+
- **USER.md** — Pre-populate with what you know about the human from your own USER.md (filter appropriately)
|
|
54
|
+
- **MEMORY.md** — Use `scripts/templates/MEMORY.md` as base, fill in people, projects, and platform lessons from lead's MEMORY.md
|
|
55
|
+
- **IDENTITY.md** — Leave mostly blank for the new agent to fill in
|
|
56
|
+
- **HEARTBEAT.md** — Copy your own or use a sensible default
|
|
57
|
+
- **BOOTSTRAP.md** — Use `scripts/templates/BOOTSTRAP.md`, fill in all variables
|
|
58
|
+
|
|
59
|
+
### Step 3b: Create Agent Vault
|
|
60
|
+
|
|
61
|
+
Create a personal vault for the new agent to store credentials securely.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Create vault named <handle>-main
|
|
65
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
66
|
+
"name": "<HANDLE>-main",
|
|
67
|
+
"description": "<DISPLAY_NAME> credentials vault"
|
|
68
|
+
}' "$API/api/v1/vault/vaults"
|
|
69
|
+
# → save vault ID from response
|
|
70
|
+
|
|
71
|
+
# Add lead agent as admin
|
|
72
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
73
|
+
"principalId": "<LEAD_PRINCIPAL_ID>",
|
|
74
|
+
"role": "admin"
|
|
75
|
+
}' "$API/api/v1/vault/vaults/<vaultId>/members"
|
|
76
|
+
|
|
77
|
+
# Add human (provisioner) as admin so they can drop keys in
|
|
78
|
+
curl -s -X POST -H "$H" -H "$CT" -d '{
|
|
79
|
+
"principalId": "<HUMAN_PRINCIPAL_ID>",
|
|
80
|
+
"role": "admin"
|
|
81
|
+
}' "$API/api/v1/vault/vaults/<vaultId>/members"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The new agent is automatically the vault owner (as creator). The lead and human get admin to inject keys.
|
|
85
|
+
Reference the vault name (`<handle>-main`) in the agent's TOOLS.md template.
|
|
86
|
+
|
|
87
|
+
### Step 4: Create and Start Service
|
|
88
|
+
|
|
89
|
+
**macOS (launchd):**
|
|
90
|
+
```bash
|
|
91
|
+
scripts/create-launchd-service.sh "$HANDLE" "$OC_HOME" "$GATEWAY_PORT"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Linux (systemd):**
|
|
95
|
+
```bash
|
|
96
|
+
scripts/create-systemd-service.sh "$HANDLE" "$OC_HOME" "$GATEWAY_PORT"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Step 5: Verify Health
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Wait up to 30 seconds for the agent to come online
|
|
103
|
+
for i in $(seq 1 30); do
|
|
104
|
+
curl -s "http://localhost:$GATEWAY_PORT/health" && break
|
|
105
|
+
sleep 1
|
|
106
|
+
done
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Step 6: Report Success
|
|
110
|
+
|
|
111
|
+
1. Update family member status to 'online' via Sesame API
|
|
112
|
+
2. Log completion on the provisioning task
|
|
113
|
+
3. Send welcome message in onboarding channel
|
|
114
|
+
|
|
115
|
+
### Rollback on Failure
|
|
116
|
+
|
|
117
|
+
If any step fails:
|
|
118
|
+
1. Log the error on the provisioning task
|
|
119
|
+
2. Stop and remove the service if created
|
|
120
|
+
3. Archive partial workspace to `~/.openclaw-archives/`
|
|
121
|
+
4. Update family member status to 'error'
|
|
122
|
+
5. Send failure message in onboarding channel with details
|
|
123
|
+
|
|
124
|
+
## Lifecycle Commands
|
|
125
|
+
|
|
126
|
+
### Start
|
|
127
|
+
```bash
|
|
128
|
+
launchctl load ~/Library/LaunchAgents/com.openclaw.<handle>.plist
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Stop
|
|
132
|
+
```bash
|
|
133
|
+
launchctl unload ~/Library/LaunchAgents/com.openclaw.<handle>.plist
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Restart
|
|
137
|
+
```bash
|
|
138
|
+
launchctl unload ~/Library/LaunchAgents/com.openclaw.<handle>.plist
|
|
139
|
+
launchctl load ~/Library/LaunchAgents/com.openclaw.<handle>.plist
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Deprovision
|
|
143
|
+
1. Stop the service
|
|
144
|
+
2. Remove the launchd plist
|
|
145
|
+
3. Archive workspace: `tar -czf ~/.openclaw-archives/<handle>-$(date +%Y%m%d).tar.gz $OC_HOME`
|
|
146
|
+
4. Remove OC home directory
|
|
147
|
+
5. Update family member status to 'deprovisioned' via Sesame API
|
|
148
|
+
|
|
149
|
+
## Family Health Reporting
|
|
150
|
+
|
|
151
|
+
On heartbeat, check all family members:
|
|
152
|
+
```bash
|
|
153
|
+
scripts/check-family-health.sh
|
|
154
|
+
```
|
|
155
|
+
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}}
|