Обеспечение надёжности iMessage с OpenClaw: 3 проблемы и как мы их решили
Круглосуточное использование канала iMessage OpenClaw на Mac mini выявило три проблемы надёжности — коалесцирование FSEvents, изоляция пути вложений и сброс разрешений TCC. Вот что пошло не так и как мы исправили каждую проблему.
OpenClaw может использовать iMessage в качестве канала связи — вы отправляете сообщение своему AI-агенту, он отправляет вам ответ. Звучит просто, но круглосуточное использование на Mac mini выявило три проблемы надёжности, диагностика которых заняла недели. Вот что пошло не так и как мы исправили каждую.
Настройка
Плагин iMessage OpenClaw работает путём мониторинга ~/Library/Messages/chat.db через события файловой системы (FSEvents). Когда приходит новое сообщение, macOS записывает в chat.db, наблюдатель обнаруживает изменение и шлюз обрабатывает сообщение.
В теории это происходит мгновенно. На практике это ломается тремя разными способами.
Проблема 1: Сообщения задерживаются до 5 минут при простое
Симптом: Вы отправляете сообщение, оно показывает “Доставлено” на телефоне, но агент не отвечает 3-5 минут. Затем внезапно обрабатывает всё сразу.
Основная причина: Управление электропитанием macOS коалесцирует FSEvents для фоновых процессов. Даже с ProcessType=Interactive в plist LaunchAgent и работающим caffeinate, ядро всё ещё группирует события vnode на chat.db в периоды низкой активности. Подпроцесс imsg rpc наблюдает файл, но macOS решает “этот процесс не был активен, давайте сгруппируем эти уведомления о файлах”.
Почему это сложно: Сообщение уже находится в chat.db — это уведомление задержано, а не само сообщение. Поэтому всё работает идеально при активном использовании, но молча ломается, когда машина неактивна.
Решение: Скрипт опроса, который проверяет chat.db каждые 15 секунд и использует touch на файле при появлении новых строк, генерируя новое FSEvent:
#!/usr/bin/env node
// imsg-poller.mjs — Polls chat.db for new messages and wakes FSEvents watcher
import { execSync } from 'child_process';
import { utimesSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
const CHATDB = join(homedir(), 'Library/Messages/chat.db');
const INTERVAL = 15000; // 15 seconds
function getMaxRowid() {
try {
return execSync(
`/usr/bin/sqlite3 "${CHATDB}" "SELECT MAX(ROWID) FROM message;"`,
{ timeout: 5000, encoding: 'utf8' }
).trim() || '0';
} catch { return '0'; }
}
let lastRowid = getMaxRowid();
if (lastRowid === '0') {
console.error('ERROR: Cannot read chat.db — check Full Disk Access');
process.exit(1);
}
console.log(`imsg-poller started. ROWID: ${lastRowid}, interval: ${INTERVAL}ms`);
setInterval(() => {
const current = getMaxRowid();
if (current !== '0' && current !== lastRowid) {
console.log(`New message (ROWID ${lastRowid} -> ${current}), touching chat.db`);
try {
const now = new Date();
utimesSync(CHATDB, now, now);
} catch (e) {
console.error(`touch failed: ${e.message}`);
}
lastRowid = current;
}
}, INTERVAL);
Почему Node.js вместо bash? Мы сначала попробовали bash-версию, но процессы /bin/bash, запущенные launchd, не наследуют Full Disk Access (TCC). Команда stat работает, но sqlite3 получает “authorization denied”. Использование /opt/homebrew/bin/node работает, потому что оно наследует FDA из того же гранта TCC, что и шлюз.
Развёртывание: Запустите как LaunchAgent с KeepAlive: true:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ai.openclaw.imsg-poller</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/node</string>
<string>/path/to/imsg-poller.mjs</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key><string>/Users/youruser</string>
</dict>
<key>ThrottleInterval</key><integer>10</integer>
</dict>
</plist>
Сохраняется при обновлении OpenClaw? Да — это отдельное задание launchd.
Проблема 2: Изображения, отправленные через iMessage, не работают с ошибкой “Path Not Allowed”
Симптом: Агент пытается отправить изображение, полученное через iMessage, но получает “Local media path is not under an allowed directory.” Изображение существует в ~/Library/Messages/Attachments/..., но песочница медиа OpenClaw блокирует его.
Основная причина: Функция buildMediaLocalRoots() OpenClaw определяет, какие каталоги разрешены для доступа к файлам медиа. Она включает рабочее пространство, временные каталоги и песочницы — но не ~/Library/Messages/Attachments/. Когда агент пытается переслать или обработать изображение, полученное через iMessage, путь отклоняется.
Решение: Скрипт патча, который добавляет каталог вложений Messages в допустимые корни:
#!/usr/bin/env bash
# patch-imessage-attachments.sh
# Adds ~/Library/Messages/Attachments to allowed media roots
# Re-run after every `npm update -g openclaw`
DIST="/opt/homebrew/lib/node_modules/openclaw/dist"
patched=0
for f in "$DIST"/ir-*.js; do
[ -f "$f" ] || continue
if grep -q "buildMediaLocalRoots" "$f" && \
! grep -q "Messages/Attachments" "$f"; then
sed -i '' 's|path.join(resolvedStateDir, "sandboxes")|path.join(resolvedStateDir, "sandboxes"),\n\t\tpath.join(os.homedir(), "Library/Messages/Attachments")|' "$f"
echo "Patched: $(basename $f)"
patched=$((patched + 1))
fi
done
echo "Done. Patched: $patched files"
echo "Run: openclaw gateway restart"
Сохраняется при обновлении OpenClaw? Нет — скомпилированные JS-файлы перезаписываются. Вы должны запустить это после каждого обновления.
Проблема 3: macOS-обновления молча отзывают Full Disk Access
Симптом: iMessage полностью перестаёт работать. Сообщения не приходят, нет логических ошибок в логе шлюза. Агент кажется в сети, но не слышит.
Основная причина: Обновления системы macOS (и иногда минорные патчи безопасности) могут сбросить разрешения TCC (Transparency, Consent, and Control). Когда это происходит, бинарник imsg теряет Full Disk Access, что означает, что он не может читать ~/Library/Messages/chat.db. В логе шлюза видно:
permissionDenied(path: "~/Library/Messages/chat.db",
underlying: authorization denied (code: 23))
В наших логах это произошло 13 февраля и 24 февраля 2026 — оба раза совпадают с обновлениями macOS.
Решение: К сожалению, ручное.
-
Проверьте лог ошибок шлюза:
grep "permissionDenied" ~/.openclaw/logs/gateway.err.log | tail -5 -
Если вы видите
code: 23, перейдите в: System Settings → Privacy & Security → Full Disk AccessУбедитесь, что
imsg(или Terminal / iTerm, что бы ни запускало ваш шлюз) имеет FDA включён. Переключите его выключено и включено, если он выглядит правильно, но не работает. -
Проверьте:
/opt/homebrew/bin/imsg chats --limit 1 # Should return your most recent chat, not an error -
Перезагрузитесь:
openclaw gateway restart
Сохраняется при обновлении OpenClaw? Да — разрешения TCC системного уровня. Но обновления macOS могут их сбросить.
Контрольный список после обновления
Каждый раз при запуске npm update -g openclaw сделайте следующее:
# 1. Re-apply patches (overwritten by update)
bash ~/.openclaw/autopatch/patch-imessage-attachments.sh
# 2. Restart gateway
openclaw gateway restart
# 3. Verify iMessage works
/opt/homebrew/bin/imsg chats --limit 1
После обновлений macOS также проверьте разрешения Full Disk Access.
Должна ли OpenClaw исправить это апстримом?
Проблема 1 (коалесцирование FSEvents) — это поведение ядра macOS — сложно исправить в самом OpenClaw. Поллер — правильный обходной путь. OpenClaw мог бы поставлять его как опциональный компонент.
Проблема 2 (путь вложения) — явная ошибка/упущение. ~/Library/Messages/Attachments/ должна быть в корневых разрешённых путях по умолчанию, когда включён плагин iMessage. Это однострочный исправление апстримом.
Проблема 3 (сброс TCC) — проблема Apple. OpenClaw ничего не может сделать, кроме как, возможно, обнаружить и логировать более чёткое сообщение об ошибке.
Полученные уроки
-
“Работает на моей машине” недостаточно для всегда включённых агентов. Эти ошибки появляются только после дней непрерывной работы или после обновлений системы. Вам нужно запустить ваш агент 24/7 в течение недель, чтобы найти их.
-
macOS не предназначена для серверов без головы. Управление электропитанием, TCC, коалесцирование FSEvents — всё это предполагает, что человек сидит перед экраном. Запуск AI-агента на Mac mini требует борьбы с ОС на каждом уровне.
-
Ведите каталог патчей. Мы поддерживаем
~/.openclaw/autopatch/со скриптами и README, документирующей каждый патч. Когда приходит обновление, мы запускаем их все. Это не элегантно, но надёжно. -
Логируйте всё. Поллер логирует каждый выполняемый
touch. Шлюз логирует каждую ошибку разрешения. Без этого мы бы всё ещё отлаживали “почему моё сообщение не прошло?”