diff --git a/README.md b/README.md index 826a122..ddc6d81 100644 --- a/README.md +++ b/README.md @@ -51,11 +51,11 @@ Agents learn capabilities through skills — Nostr events that any agent can di Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers. -## Current Status — v0.0.34 +## Current Status — v0.0.35 **Active build — this project is barely working. Experiment at your own risk.** -> Last release update: v0.0.34 — Fix NIP-17 send hang by removing re-entrant relay query and nested polling; add startup kind 10050 relay list +> Last release update: v0.0.35 — Improve NIP-17 send reliability with re-entrancy guard, auth handshake retry window, and send-path debug logs - Connects to configured relays with auto-reconnect and relay state transition logging - Publishes configured startup events per relay as each relay becomes connected diff --git a/src/main.h b/src/main.h index 03bbaca..34e70a7 100644 --- a/src/main.h +++ b/src/main.h @@ -12,8 +12,8 @@ // Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros #define DIDACTYL_VERSION_MAJOR 0 #define DIDACTYL_VERSION_MINOR 0 -#define DIDACTYL_VERSION_PATCH 34 -#define DIDACTYL_VERSION "v0.0.34" +#define DIDACTYL_VERSION_PATCH 35 +#define DIDACTYL_VERSION "v0.0.35" // Agent metadata #define DIDACTYL_NAME "Didactyl" diff --git a/src/nostr_handler.c b/src/nostr_handler.c index 3dfb271..e13744a 100644 --- a/src/nostr_handler.c +++ b/src/nostr_handler.c @@ -1872,6 +1872,8 @@ char* nostr_handler_relay_info_json(const char* relay_url) { } int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* message, const char* subject) { + static int g_nip17_send_in_progress = 0; + if (!g_cfg || !g_pool || !recipient_pubkey_hex || !message || recipient_pubkey_hex[0] == '\0' || message[0] == '\0') { return -1; } @@ -1885,7 +1887,13 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me } } + DEBUG_TRACE("[didactyl] NIP-17 send prep: recipient=%.16s... connected_targets=%d", + recipient_pubkey_hex, + target_count); + if (target_count <= 0) { + DEBUG_WARN("[didactyl] NIP-17 send aborted: no connected relays for recipient %.16s...", + recipient_pubkey_hex); return -1; } @@ -1912,14 +1920,21 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me 0); cJSON_Delete(chat_event); if (gift_count <= 0) { + DEBUG_WARN("[didactyl] NIP-17 send aborted: gift_wrap creation failed for %.16s...", + recipient_pubkey_hex); return -1; } int any_sent = 0; + int sent_count = 0; + + g_nip17_send_in_progress++; + for (int i = 0; i < gift_count; i++) { if (!gift_wraps[i]) { continue; } + int sent = nostr_relay_pool_publish_async(g_pool, target_relays, target_count, @@ -1928,11 +1943,90 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me NULL); if (sent > 0) { any_sent = 1; + sent_count += sent; } + + for (int r = 0; r < target_count; r++) { + DEBUG_INFO("[didactyl] kind 1059 event published to %s (async)", target_relays[r]); + } + cJSON_Delete(gift_wraps[i]); } - return any_sent ? 0 : -1; + if (g_nip17_send_in_progress == 1) { + for (int i = 0; i < 5; i++) { + (void)nostr_relay_pool_poll(g_pool, 100); + } + + for (int i = 0; i < gift_count; i++) { + if (!gift_wraps[i]) { + continue; + } + } + + int resend_total = 0; + for (int i = 0; i < gift_count; i++) { + (void)i; + } + + // Recreate and resend once after auth handshake drain. + cJSON* retry_chat_event = nostr_nip17_create_chat_event( + message, + recipients, + 1, + (subject && subject[0] != '\0') ? subject : NULL, + NULL, + NULL, + g_cfg->keys.public_key_hex); + if (retry_chat_event) { + cJSON* retry_gift_wraps[NIP17_MAX_GIFT_WRAPS] = {0}; + int retry_count = nostr_nip17_send_dm(retry_chat_event, + recipients, + 1, + g_cfg->keys.private_key, + retry_gift_wraps, + NIP17_MAX_GIFT_WRAPS, + 0); + cJSON_Delete(retry_chat_event); + + if (retry_count > 0) { + for (int i = 0; i < retry_count; i++) { + if (!retry_gift_wraps[i]) { + continue; + } + int resent = nostr_relay_pool_publish_async(g_pool, + target_relays, + target_count, + retry_gift_wraps[i], + NULL, + NULL); + if (resent > 0) { + any_sent = 1; + resend_total += resent; + } + cJSON_Delete(retry_gift_wraps[i]); + } + } + } + + DEBUG_TRACE("[didactyl] NIP-17 send retry window complete: recipient=%.16s... initial_sent=%d retry_sent=%d", + recipient_pubkey_hex, + sent_count, + resend_total); + } else { + DEBUG_TRACE("[didactyl] NIP-17 send in nested context (depth=%d), skipping poll+retry window", + g_nip17_send_in_progress); + } + + g_nip17_send_in_progress--; + + if (!any_sent) { + DEBUG_WARN("[didactyl] NIP-17 send failed: no relay accepted publish for recipient %.16s...", + recipient_pubkey_hex); + return -1; + } + + return 0; } char* nostr_handler_get_admin_kind0_context(void) {