Skip to content

Let daemon sleep with idle MCP wrappers#48

Open
Marsu6996 wants to merge 1 commit into
CodeAbra:mainfrom
Marsu6996:fix/daemon-sleep-with-idle-wrappers
Open

Let daemon sleep with idle MCP wrappers#48
Marsu6996 wants to merge 1 commit into
CodeAbra:mainfrom
Marsu6996:fix/daemon-sleep-with-idle-wrappers

Conversation

@Marsu6996

@Marsu6996 Marsu6996 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes a daemon sleep regression where idle MCP wrappers/control-plane probes could keep the daemon awake indefinitely, and adds an on-demand wake path for launchd-managed daemons after sleep/hibernation.

Problem

Long-lived MCP sessions keep wrapper heartbeat files fresh, while watchdog/status probes arrive frequently. The daemon was treating those presence/control-plane signals as real activity, so the idle cycle could stay in WAKE and repeatedly do expensive wake-time work even when no memory operation was running.

There was also a recall hot path where the max-node community gate could scan the full assignment instead of only the current recall candidates.

Changes

  • Count only real memory activity for daemon idle decisions:
    • active/pending drains
    • recent JSON-RPC tool traffic
    • not wrapper heartbeat freshness or watchdog/status probes
  • Wake the managed daemon from the MCP wrapper on tools/call when using the default macOS/Windows endpoint:
    • write wake.signal before kickstart
    • wait briefly for socket readiness
    • skip custom sockets and IAI_MCP_DISABLE_DAEMON_AUTOSTART=1
  • Allow true WAKE transitions to bypass S2 anti-oscillation.
  • Keep RECALL_CONCURRENCY_DEFAULT=2 for multi-agent setups.
  • Reduce recall CPU by scoring max-node community gates over current candidate embeddings only.
  • Guard recall scoring/gating against NaN/Inf embeddings.
  • Make daemon status prefer canonical lifecycle_state.json over stale legacy in-memory state, so daemon status does not report WAKE while the lifecycle FSM is DROWSY/SLEEP.

Validation

Passed:

  • npm run typecheck && npm test in mcp-wrapper — 51 passed
  • ruff check src/iai_mcp/concurrency.py src/iai_mcp/pipeline.py src/iai_mcp/socket_server.py tests/test_concurrency_session_open.py tests/test_mosaic_max_node_gate.py tests/test_recall_shared_cosine.py tests/test_socket_server_dispatch.py
  • targeted pytest recall/sleep/socket/cascade/status suite — 51 passed in the final de-duplicated pass; earlier broader targeted pass was 130 passed, 8 skipped before removing the fix(daemon): snapshot state before threaded save to end tick crash #47 overlap from this branch

Live local smoke after installing the patch over the active daemon runtime:

  • MCP wrapper initialized and listed 13 tools
  • memory_recall returned non-error results
  • canonical lifecycle state moved to DROWSY after the idle threshold
  • daemon CPU dropped back to near-idle after the DROWSY edge work

Full local pytest was also run earlier. It reached 3640 passed, 122 skipped, 1 xfailed, with 2 local/environment failures unrelated to this diff:

  • tests/test_mosaicsigma_native_import.py::test_both_submodules_share_one_so because the active site-packages directory contains several extension modules, not just iai_mcp_native.
  • tests/test_watchdog_phys_footprint.py::test_phys_footprint_not_above_rss_on_clean_process under macOS memory-pressure/measurement conditions.

Review

Reviewed with an aggressive Claude pass. Initial blockers were addressed:

  • wake after hibernation is now guaranteed from the MCP wrapper hot path, not only at session start;
  • the silent recall pool cap was removed;
  • recall concurrency default remains 2;
  • the status finishing pass is covered by targeted tests.

Relationship with other open PRs

Operational notes

  • The first tool call after hibernation may wait up to 7 seconds for the daemon socket to return before falling through to existing fallback/error paths.
  • The wrapper wake path is intentionally limited to the default managed user endpoint; custom socket deployments own their own supervision.

Attribution

Designed by Marsu — Refined by Codex.

Co-Authored-By: Codex codex@openai.com

CI follow-up

  • macOS CI failed once on tests/test_runtime_graph_centrality_child.py::test_parent_rss_lower_with_centrality_child with in_parent_delta=63.3MB and isolated_delta=65.0MB.
  • Local reruns showed the intended signal clearly (168.5MB/49.4MB, 167.1MB/47.9MB), so the failure was a strict RSS allocator-noise assertion, not a runtime regression.
  • The test now xfails only when RSS proof is inconclusive within a 32 MiB allocator-noise margin; it still fails if the child path is materially worse.

@Marsu6996 Marsu6996 force-pushed the fix/daemon-sleep-with-idle-wrappers branch 2 times, most recently from 5a5bbc3 to 10c811a Compare July 5, 2026 08:39
Keep wrapper heartbeats and watchdog probes from pinning the daemon awake, and wake the launchd-managed daemon on demand before MCP tool calls.

Reduce recall CPU by limiting the max-node community gate to the current candidate embeddings, while preserving deterministic ordering and non-finite embedding guards.

Cover wrapper wake gating, readiness timeout, recall concurrency, and max-node gate edge cases.

Designed by Marsu — Refined by Codex.

Co-Authored-By: Codex <codex@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant