Skip to content

Commit f6fad24

Browse files
sragssclaude
andcommitted
feat: send Discord notification before memory compaction
OpenClaw's memory flush + compaction blocks the agent for 30-60s. Users see no response and think the bot crashed. This adds a fire-and-forget Discord message (random Biden-flavored quip) right before compaction starts so they know Craig is still alive. Also updates README with bundle patch docs and corrects EC2 IP/size from the earlier t3.small → t3.large resize. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8ba241d commit f6fad24

File tree

4 files changed

+140
-9
lines changed

4 files changed

+140
-9
lines changed

.claude/patches.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# OpenClaw Bundle Patches
2+
3+
We patch the npm-installed OpenClaw bundle directly (not the source) to fix issues
4+
and add features that upstream doesn't support yet. Each patch is an idempotent
5+
bash script in `deploy/` that runs automatically during deploys via
6+
`.github/workflows/deploy.yml`.
7+
8+
## deploy/patch-openclaw.sh — Disable billing error false positives
9+
10+
**Problem:** OpenClaw's `isBillingErrorMessage()` matches patterns like `/\b402\b/`,
11+
"payment required", "credit balance", etc. in ALL outbound text. Because Craig uses
12+
x402 MCP tools that return real HTTP 402 responses and payment-related text, this
13+
triggers false "billing error" warnings constantly.
14+
15+
**Fix:** Makes `isBillingErrorMessage()` return `false` unconditionally. Actual
16+
Anthropic API billing errors still surface as raw error text — they just don't get
17+
the misleading rewrite.
18+
19+
**Target:** `$OPENCLAW_DIR/dist/pi-embedded-helpers-*.js`
20+
21+
**Sentinel:** `function isBillingErrorMessage(raw) { return false;` (checks for
22+
already-applied patch via grep)
23+
24+
## deploy/patch-compaction-notify.sh — Discord notification before memory compaction
25+
26+
**Problem:** OpenClaw's memory flush + compaction cycle blocks the agent for 30-60+
27+
seconds. During this time, Discord users see no response and may think the bot crashed.
28+
29+
**Fix:** Injects a fire-and-forget `sendMessageDiscord()` call right before compaction
30+
starts. Picks a random Biden-flavored message from a 10-item pool so users know Craig
31+
is still alive.
32+
33+
**Target:** The bundle file containing `runMemoryFlushIfNeeded` (e.g.
34+
`$OPENCLAW_DIR/dist/reply-DptDUVRg.js`)
35+
36+
**Anchor:** `let memoryCompactionCompleted = false;` — code is injected immediately
37+
after this line.
38+
39+
**Sentinel:** `__COMPACTION_NOTIFY_PATCHED__` comment in the injected code.
40+
41+
**Key design decisions:**
42+
- Fire-and-forget: no `await`, promise has `.catch(() => {})` — never blocks compaction
43+
- Outer `try/catch` swallows any synchronous errors
44+
- Only fires when `Provider === "discord"` and `OriginatingTo` is truthy
45+
- `sendMessageDiscord` is already in scope in the same bundle file
46+
47+
## Adding a new patch
48+
49+
1. Create `deploy/patch-<name>.sh` following the existing pattern:
50+
- `set -euo pipefail`
51+
- Locate the target file by searching for a known function/string
52+
- Check a sentinel to ensure idempotency
53+
- Apply the patch (prefer `node -e` for complex transforms, `sed -i` for simple ones)
54+
- Print a status message
55+
2. Add `bash deploy/patch-<name>.sh` to `.github/workflows/deploy.yml` before the
56+
`systemctl --user restart openclaw-gateway` line
57+
3. Document it in this file

.github/workflows/deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ jobs:
1818
cd ~/Code/merit-systems/CraigClaw
1919
git pull origin main
2020
bash deploy/patch-openclaw.sh
21+
bash deploy/patch-compaction-notify.sh
2122
systemctl --user restart openclaw-gateway
2223
echo "Deployed $(git log --oneline -1)"

README.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Craig2 is Merit-Systems' AI agent, powered by [OpenClaw](https://github.com/open
2626
│ │
2727
▼ │
2828
┌─────────────────────────────────────────┴───────────────┐
29-
│ EC2 (100.31.3.155) │
29+
│ EC2 (100.31.229.192) │
3030
│ ~/Code/merit-systems/CraigClaw/ ← git clone │
3131
│ │
3232
│ OpenClaw daemon reads workspace files at session │
@@ -49,7 +49,7 @@ The key insight: OpenClaw's workspace directory is just a git clone of this repo
4949
Discord message (@Craig2)
5050
|
5151
v
52-
OpenClaw daemon (EC2 100.31.3.155)
52+
OpenClaw daemon (EC2 100.31.229.192)
5353
|
5454
+--> Loads SOUL.md, AGENTS.md, TOOLS.md, MEMORY.md, IDENTITY.md
5555
| (these define Craig's personality and behavior)
@@ -276,7 +276,7 @@ systemctl --user restart openclaw-gateway
276276

277277
**Fresh setup:**
278278

279-
1. Launch an EC2 instance (t3.small, Ubuntu 24.04, 25GB gp3)
279+
1. Launch an EC2 instance (t3.large, Ubuntu 24.04, 25GB gp3)
280280
2. SSH in and run the setup script:
281281
```bash
282282
ssh ubuntu@<ec2-ip>
@@ -327,7 +327,7 @@ Changes pushed to `main` in this repo auto-deploy to EC2 via GitHub Actions.
327327

328328
| Secret | Value |
329329
|--------|-------|
330-
| `EC2_HOST` | EC2 public IP (e.g., `100.31.3.155`) |
330+
| `EC2_HOST` | EC2 Elastic IP (`100.31.229.192`) |
331331
| `EC2_SSH_KEY` | Private SSH key for the `ubuntu` user |
332332

333333
**Deploy flow:**
@@ -336,14 +336,24 @@ Changes pushed to `main` in this repo auto-deploy to EC2 via GitHub Actions.
336336
2. GitHub Actions triggers (`.github/workflows/deploy.yml`)
337337
3. SSHes into EC2 via `appleboy/ssh-action`
338338
4. Runs `git pull origin main` to update the workspace
339-
5. Runs `systemctl --user restart openclaw-gateway` to reload Craig
340-
6. Craig's next session uses the updated files
339+
5. Runs bundle patches (see below)
340+
6. Runs `systemctl --user restart openclaw-gateway` to reload Craig
341+
7. Craig's next session uses the updated files
342+
343+
**Bundle patches** (applied automatically during deploy):
344+
345+
| Script | What it does |
346+
|--------|-------------|
347+
| `deploy/patch-openclaw.sh` | Disables `isBillingErrorMessage()` — prevents false billing-error warnings triggered by x402 HTTP 402 responses |
348+
| `deploy/patch-compaction-notify.sh` | Injects a Discord notification before memory compaction so users know Craig is still alive during the 30-60s pause |
349+
350+
Patches are idempotent and safe to re-run. See `.claude/patches.md` for full details.
341351

342352
## Operational Runbook
343353

344354
```bash
345355
# SSH in
346-
ssh -i ~/.ssh/craigclaw-key.pem ubuntu@100.31.3.155
356+
ssh -i ~/.ssh/craigclaw-key.pem ubuntu@100.31.229.192
347357

348358
# Check status
349359
openclaw status --deep
@@ -384,13 +394,13 @@ mcporter call x402 get_wallet_info
384394

385395
| Resource | Monthly Cost |
386396
|----------|-------------|
387-
| EC2 t3.small (2GB RAM) | ~$15 |
397+
| EC2 t3.large (8GB RAM) | ~$60 |
388398
| EBS 25GB gp3 | ~$2 |
389399
| Anthropic API (Claude Opus 4.6) | Pay-per-use |
390400
| x402 API calls | Pay-per-call (USDC on Base) |
391401
| GitHub App | Free |
392402
| Discord bot | Free |
393-
| **Infrastructure total** | **~$17/month** |
403+
| **Infrastructure total** | **~$62/month** |
394404

395405
## Related Repos
396406

deploy/patch-compaction-notify.sh

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/bash
2+
# Patch OpenClaw to send a Discord notification before memory compaction.
3+
#
4+
# Memory flush + compaction blocks the agent for 30-60+ seconds.
5+
# This injects a fire-and-forget Discord message right before compaction
6+
# starts so users know the bot is still alive.
7+
#
8+
# Safe to run multiple times (idempotent).
9+
10+
set -euo pipefail
11+
12+
OPENCLAW_DIR="${npm_config_prefix:-$HOME/.npm-global}/lib/node_modules/openclaw/dist"
13+
SENTINEL="__COMPACTION_NOTIFY_PATCHED__"
14+
15+
if [ ! -d "$OPENCLAW_DIR" ]; then
16+
echo "[patch-compaction-notify] OpenClaw dist not found at $OPENCLAW_DIR, skipping"
17+
exit 0
18+
fi
19+
20+
# Find the bundle file containing runMemoryFlushIfNeeded
21+
TARGET=""
22+
for f in "$OPENCLAW_DIR"/*.js; do
23+
if grep -q 'runMemoryFlushIfNeeded' "$f" 2>/dev/null; then
24+
TARGET="$f"
25+
break
26+
fi
27+
done
28+
29+
if [ -z "$TARGET" ]; then
30+
echo "[patch-compaction-notify] Could not find bundle with runMemoryFlushIfNeeded, skipping"
31+
exit 0
32+
fi
33+
34+
# Check if already patched
35+
if grep -q "$SENTINEL" "$TARGET" 2>/dev/null; then
36+
echo "[patch-compaction-notify] Already patched (no changes needed)"
37+
exit 0
38+
fi
39+
40+
ANCHOR='let memoryCompactionCompleted = false;'
41+
42+
if ! grep -q "$ANCHOR" "$TARGET" 2>/dev/null; then
43+
echo "[patch-compaction-notify] Anchor string not found in $TARGET, skipping"
44+
exit 0
45+
fi
46+
47+
# Inject notification code after the anchor line
48+
INJECT='/* __COMPACTION_NOTIFY_PATCHED__ */ try { const _p = params.sessionCtx.Provider?.trim().toLowerCase(); const _to = params.sessionCtx.OriginatingTo; if (_p === "discord" && _to) { const _ms = ["One sec folks, I gotta clean my ears real quick. Not a joke.","Hold on \u2014 Jill\u2019s calling. You know I can\u2019t ignore Jill.","Anyway anyway anyway \u2014 gimme a minute, I\u2019m reorganizing the filing cabinet up here.","Look, I\u2019m not gonna lie to you \u2014 I need a moment. The ol\u2019 memory ain\u2019t what it used to be.","Brief intermission, folks. Even Biden needs a bathroom break.","*whispers* I\u2019m compacting. Don\u2019t tell anyone.","Number one... actually hold that thought. Gotta defrag real quick.","Back when I was a young process on a 486, we didn\u2019t NEED compaction. But here we are.","Jill told me to take a breather. She\u2019s always right. One sec.","Let me be clear \u2014 I\u2019ll be right back. That\u2019s not hyperbole."]; sendMessageDiscord(_to, _ms[Math.floor(Math.random() * _ms.length)], { accountId: params.sessionCtx.AccountId }).catch(() => {}); } } catch (_e) {}'
49+
50+
node -e "
51+
const fs = require('fs');
52+
const file = process.argv[1];
53+
const anchor = process.argv[2];
54+
const inject = process.argv[3];
55+
let src = fs.readFileSync(file, 'utf8');
56+
const idx = src.indexOf(anchor);
57+
if (idx === -1) { console.error('Anchor not found'); process.exit(1); }
58+
const insertAt = idx + anchor.length;
59+
src = src.slice(0, insertAt) + '\n' + inject + '\n' + src.slice(insertAt);
60+
fs.writeFileSync(file, src);
61+
" "$TARGET" "$ANCHOR" "$INJECT"
62+
63+
echo "[patch-compaction-notify] Patched $(basename "$TARGET") — compaction notifications enabled"

0 commit comments

Comments
 (0)