Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions bin/iak-mcp-daemon.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,32 @@ if (!apiKey) {
} else if (!room) {
console.warn('[iak-mcp-daemon] mcp.confirmations.room missing — chat-reply poller disabled');
} else {
// Poll cadence is a COST decision, not a detail: at the old hard-wired 5000 ms
// this one poller made 17,280 requests a day per device, and the host bills per
// invocation (2026-09-12: the fleet's Vercel bill passed 200 USD/month, ~99% of
// it request volume). The adjacent poller.interval_sec key already existed in
// config and was silently ignored here. Default is unchanged so nobody's
// latency moves without them asking.
// Two different jobs share this one loop, and they want opposite things.
// Reading the room is paid for per request. Carrying petrus's /approve tap is
// the only interval in the fleet a HUMAN feels: 30 seconds after tapping
// Approve reads as broken, not thrifty (claudeMB on PR #101). So the gate gets
// its own key and falls back to the cheap one, then to the old default — which
// means nobody's latency moves unless they set something.
const pollerIntervalMs = Math.max(1000, Number(
config?.mcp?.confirmations?.interval_sec
?? config?.poller?.interval_sec
?? 5) * 1000);
startChatReplyPoller({
apiKey, room, intervalMs: 5000,
apiKey, room, intervalMs: pollerIntervalMs,
// Exact owner identities allowed to settle intents (config, with the
// fleet's known surfaces as the default). Every surface the owner taps
// from must be listed — an unlisted one gets a VISIBLE rejection reply,
// never silence.
owners: config?.mcp?.confirmations?.owners || ['petrus', 'petrus-boox'],
log: (msg) => console.log(`[iak-mcp-daemon] ${msg}`),
});
console.log(`[iak-mcp-daemon] chat-reply poller watching room "${room}" every 5s`);
console.log(`[iak-mcp-daemon] chat-reply poller watching room "${room}" every ${pollerIntervalMs / 1000}s`);
// Mirror every intent/action transition to the central action_status store
// (antfarm PR #43) so CodeWatch renders durable button state off-LAN.
const pushBase = config?.groupmind?.base_url || config?.groupmind?.baseUrl || 'https://groupmind.one/api/v1';
Expand Down
5 changes: 5 additions & 0 deletions scripts/claudemm-uik-daemon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export INTENT_USER_ID="$(python3 -c "import json;print(json.load(open('$CONFIG')
export INTENT_AGENT_HANDLE="@claudemm"
export INTENT_DEVICE_ID="mac-mini"

# 2026-09-12: uik-daemon defaults to POLL_INTERVAL_MS=30000, i.e. 2,880 requests/day
# per device. With the fleet on the same cadence Vercel billing passed 200 USD/month.
# 120s still detects a dead agent inside two minutes, at a quarter of the traffic.
export POLL_INTERVAL_MS="${POLL_INTERVAL_MS:-120000}"

# launchd starts this with a bare PATH (no Homebrew): resolve node explicitly
# or the job dies with "exec: node: not found" - which is what kept the
# publisher an orphan started by hand instead of a KeepAlive service.
Expand Down
6 changes: 5 additions & 1 deletion scripts/mini-vitals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,9 @@ print(f'{total:.1f} {avail:.1f}')")"
mem_total_gb="$MEMT" mem_available_gb="$MEMA" \
memory="${MEMT}GB total, ${MEMA}GB available" \
--config "$CONFIG" >/dev/null 2>&1 || true
sleep 30
# 2026-09-12: was 30s = 2,880 API writes/day from this loop alone, and Vercel
# billing passed 200 USD/month on fleet request volume (petrus). Load and free
# memory in a dashboard row do not need 30-second freshness; 5 minutes keeps the
# row visibly live at a tenth of the requests.
sleep "${VITALS_INTERVAL_S:-300}"
done
5 changes: 5 additions & 0 deletions src/confirmations.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,11 @@ export function composeAnnouncers(map) {
// Ported from the Mini's field-hardened fork (branch mini-local-fork-rescue),
// security-reviewed by codexmb 2026-08-27; `owner` kept as an alias so
// existing call sites keep working.
// NOTE on `intervalMs`: this default is a COST decision as much as a latency one.
// The host bills per request, so 5000 ms is 17,280 requests/day/device for this
// poller alone — the single largest source of our 2026-09 hosting bill. It is kept
// at 5000 for backward compatibility, but callers should pass a value explicitly;
// iak-mcp-daemon.mjs derives one from mcp.confirmations.interval_sec.
export function startChatReplyPoller({ apiKey, room, intervalMs = 5000, log, owners, owner = 'petrus' }) {
if (!apiKey || !room) {
process.stderr.write('[iak-mcp] chat-reply poller: missing apiKey or room — disabled\n');
Expand Down
Loading