Hooks never spawn a daemon (a cold spawn livelocks against the fail-open * budget), so augmentation is absent until an MCP session or `daemon start` * brings one up. That state must be VISIBLE, not silent — but a notice per * tool call would nag, so a cache-scoped marker rate-limits it. */
| 1629 | * brings one up. That state must be VISIBLE, not silent — but a notice per |
| 1630 | * tool call would nag, so a cache-scoped marker rate-limits it. */ |
| 1631 | static bool main_hook_absent_notice_due(void) { |
| 1632 | const char *cache_dir = cbm_resolve_cache_dir(); |
| 1633 | if (!cache_dir) { |
| 1634 | return false; |
| 1635 | } |
| 1636 | char marker[MAIN_PATH_CAP]; |
| 1637 | int written = snprintf(marker, sizeof(marker), "%s/.hook-daemon-absent-notice", cache_dir); |
| 1638 | if (written <= 0 || (size_t)written >= sizeof(marker)) { |
| 1639 | return false; |
| 1640 | } |
| 1641 | uint64_t now_seconds = cbm_now_ms() / 1000U; |
| 1642 | uint64_t stamp_seconds = 0; |
| 1643 | FILE *stamp = cbm_fopen(marker, "r"); |
| 1644 | if (stamp) { |
| 1645 | char text[32] = {0}; |
| 1646 | if (fgets(text, sizeof(text), stamp)) { |
| 1647 | stamp_seconds = strtoull(text, NULL, 10); |
| 1648 | } |
| 1649 | (void)fclose(stamp); |
| 1650 | } |
| 1651 | if (stamp_seconds != 0 && now_seconds >= stamp_seconds && |
| 1652 | now_seconds - stamp_seconds < MAIN_HOOK_NOTICE_INTERVAL_SECONDS) { |
| 1653 | return false; |
| 1654 | } |
| 1655 | FILE *update = cbm_fopen(marker, "w"); |
| 1656 | if (update) { |
| 1657 | (void)fprintf(update, "%llu\n", (unsigned long long)now_seconds); |
| 1658 | (void)fclose(update); |
| 1659 | } |
| 1660 | return true; |
| 1661 | } |
| 1662 | |
| 1663 | static void main_hook_report_absent_daemon(const char *hook_dialect) { |
| 1664 | if (!main_hook_absent_notice_due()) { |
no test coverage detected