From b6ca2bf3bf59685c049f2ebd32d93af9422e17ff Mon Sep 17 00:00:00 2001 From: Didactyl User Date: Tue, 21 Apr 2026 06:35:53 -0400 Subject: [PATCH] v0.2.47 - nostr: exclude admin from kind 10123 self-skill adoption and add self-skill upsert debug logs --- README.md | 4 +-- src/main.h | 4 +-- src/nostr_handler.c | 63 ++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 497ed75..78a6604 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,11 @@ Skills compose by adoption-list order (`10123`) and trigger tags carry runtime e 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.2.46 +## Current Status — v0.2.47 **Active build — this project is barely working. Experiment at your own risk.** -> Last release update: v0.2.46 — Finalize skill_list formatting cleanup and adopted-author cache refresh improvements +> Last release update: v0.2.47 — nostr: exclude admin from kind 10123 self-skill adoption and add self-skill upsert 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 6503ce6..6e79b67 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 2 -#define DIDACTYL_VERSION_PATCH 46 -#define DIDACTYL_VERSION "v0.2.46" +#define DIDACTYL_VERSION_PATCH 47 +#define DIDACTYL_VERSION "v0.2.47" // Agent metadata #define DIDACTYL_NAME "Didactyl" diff --git a/src/nostr_handler.c b/src/nostr_handler.c index 5f1a380..de6fc4f 100644 --- a/src/nostr_handler.c +++ b/src/nostr_handler.c @@ -1510,6 +1510,7 @@ static void register_trigger_from_self_skill_event(cJSON* event) { static void self_skill_cache_upsert_event_locked(cJSON* event) { if (!event || !cJSON_IsObject(event)) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: event missing or not object"); return; } @@ -1517,36 +1518,51 @@ static void self_skill_cache_upsert_event_locked(cJSON* event) { cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(event, "pubkey"); cJSON* tags = cJSON_GetObjectItemCaseSensitive(event, "tags"); cJSON* created_at = cJSON_GetObjectItemCaseSensitive(event, "created_at"); + cJSON* ev_id = cJSON_GetObjectItemCaseSensitive(event, "id"); + + int kind_val = (kind && cJSON_IsNumber(kind)) ? (int)kind->valuedouble : -1; + double created = (created_at && cJSON_IsNumber(created_at)) ? created_at->valuedouble : 0.0; + + DEBUG_INFO("[didactyl] self-skill upsert candidate: kind=%d author=%.16s... id=%.16s... created_at=%.0f", + kind_val, + (pubkey && cJSON_IsString(pubkey) && pubkey->valuestring) ? pubkey->valuestring : "?", + (ev_id && cJSON_IsString(ev_id) && ev_id->valuestring) ? ev_id->valuestring : "?", + created); if (!kind || !cJSON_IsNumber(kind) || !pubkey || !cJSON_IsString(pubkey) || !pubkey->valuestring || !tags || !cJSON_IsArray(tags)) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: missing required kind/pubkey/tags"); return; } - int kind_val = (int)kind->valuedouble; if (kind_val != 31123 && kind_val != 31124 && kind_val != 10123) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: kind not in {31123,31124,10123}"); return; } if (!g_cfg) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: config unavailable"); return; } int is_agent_author = (strcmp(pubkey->valuestring, g_cfg->keys.public_key_hex) == 0); int is_admin_author = (g_cfg->admin.pubkey[0] != '\0' && strcmp(pubkey->valuestring, g_cfg->admin.pubkey) == 0); if (kind_val == 10123) { - if (!is_agent_author && !is_admin_author) { + if (!is_agent_author) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: kind=10123 author is not self"); return; } } if (is_admin_author && kind_val == 31124) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: admin cannot own private skill (31124)"); return; } const char* d_tag = find_tag_value_local(tags, "d"); if ((kind_val == 31123 || kind_val == 31124) && (!d_tag || d_tag[0] == '\0')) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: parameterized kind missing d-tag"); return; } @@ -1554,12 +1570,11 @@ static void self_skill_cache_upsert_event_locked(cJSON* event) { cJSON_Delete(g_self_skill_events); g_self_skill_events = cJSON_CreateArray(); if (!g_self_skill_events) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: failed to create cache array"); return; } } - double created = (created_at && cJSON_IsNumber(created_at)) ? created_at->valuedouble : 0.0; - int n = cJSON_GetArraySize(g_self_skill_events); for (int i = 0; i < n; i++) { cJSON* existing = cJSON_GetArrayItem(g_self_skill_events, i); @@ -1591,23 +1606,56 @@ static void self_skill_cache_upsert_event_locked(cJSON* event) { cJSON* ex_created_at = cJSON_GetObjectItemCaseSensitive(existing, "created_at"); double ex_created = (ex_created_at && cJSON_IsNumber(ex_created_at)) ? ex_created_at->valuedouble : 0.0; if (created < ex_created) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: stale (existing created_at=%.0f >= new created_at=%.0f)", + ex_created, + created); return; } cJSON* dup = cJSON_Duplicate(event, 1); if (!dup) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: failed to duplicate event"); return; } cJSON_ReplaceItemInArray(g_self_skill_events, i, dup); + DEBUG_INFO("[didactyl] self-skill upsert accepted: replaced existing (old created_at=%.0f)", ex_created); + if (kind_val == 10123) { + int a_count = 0; + cJSON* t; + cJSON_ArrayForEach(t, tags) { + if (cJSON_IsArray(t) && cJSON_GetArraySize(t) >= 2) { + cJSON* t0 = cJSON_GetArrayItem(t, 0); + if (t0 && cJSON_IsString(t0) && t0->valuestring && strcmp(t0->valuestring, "a") == 0) { + a_count++; + } + } + } + DEBUG_INFO("[didactyl] kind 10123 accepted with %d 'a' tags", a_count); + } return; } cJSON* dup = cJSON_Duplicate(event, 1); if (!dup) { + DEBUG_INFO("[didactyl] self-skill upsert rejected: failed to duplicate event"); return; } cJSON_AddItemToArray(g_self_skill_events, dup); + DEBUG_INFO("[didactyl] self-skill upsert accepted: new entry added"); + if (kind_val == 10123) { + int a_count = 0; + cJSON* t; + cJSON_ArrayForEach(t, tags) { + if (cJSON_IsArray(t) && cJSON_GetArraySize(t) >= 2) { + cJSON* t0 = cJSON_GetArrayItem(t, 0); + if (t0 && cJSON_IsString(t0) && t0->valuestring && strcmp(t0->valuestring, "a") == 0) { + a_count++; + } + } + } + DEBUG_INFO("[didactyl] kind 10123 accepted with %d 'a' tags", a_count); + } } static int hex_to_pubkey(const char* hex, unsigned char out_pubkey[32]) { @@ -2775,6 +2823,10 @@ int nostr_handler_subscribe_self_skills(void) { cJSON_AddItemToObject(filter, "authors", authors); cJSON_AddNumberToObject(filter, "limit", 300); + char* filter_str = cJSON_PrintUnformatted(filter); + DEBUG_INFO("[didactyl] self-skill subscription filter: %s", filter_str ? filter_str : "(null)"); + free(filter_str); + pthread_mutex_lock(&g_subscription_mutex); managed_subscriptions_init_defaults(); if (managed_subscription_register_locked(MANAGED_SUB_SELF_SKILLS, @@ -3719,9 +3771,6 @@ int nostr_handler_refresh_self_skill_cache_from_relays(int timeout_ms) { cJSON_AddItemToObject(filter, "kinds", kinds); cJSON_AddItemToArray(authors, cJSON_CreateString(g_cfg->keys.public_key_hex)); - if (g_cfg->admin.pubkey[0] != '\0' && strcmp(g_cfg->admin.pubkey, g_cfg->keys.public_key_hex) != 0) { - cJSON_AddItemToArray(authors, cJSON_CreateString(g_cfg->admin.pubkey)); - } cJSON_AddItemToObject(filter, "authors", authors); cJSON_AddNumberToObject(filter, "limit", 500);