v0.2.45 - Expand skill refresh to include adopted authors and clean up skill_list output
This commit is contained in:
@@ -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.
|
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.44
|
## Current Status — v0.2.45
|
||||||
|
|
||||||
**Active build — this project is barely working. Experiment at your own risk.**
|
**Active build — this project is barely working. Experiment at your own risk.**
|
||||||
|
|
||||||
> Last release update: v0.2.44 — Fix adoption list selection to self-authored latest event in agent and skill tool
|
> Last release update: v0.2.45 — Expand skill refresh to include adopted authors and clean up skill_list output
|
||||||
|
|
||||||
- Connects to configured relays with auto-reconnect and relay state transition logging
|
- Connects to configured relays with auto-reconnect and relay state transition logging
|
||||||
- Publishes configured startup events per relay as each relay becomes connected
|
- Publishes configured startup events per relay as each relay becomes connected
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||||
#define DIDACTYL_VERSION_MAJOR 0
|
#define DIDACTYL_VERSION_MAJOR 0
|
||||||
#define DIDACTYL_VERSION_MINOR 2
|
#define DIDACTYL_VERSION_MINOR 2
|
||||||
#define DIDACTYL_VERSION_PATCH 44
|
#define DIDACTYL_VERSION_PATCH 45
|
||||||
#define DIDACTYL_VERSION "v0.2.44"
|
#define DIDACTYL_VERSION "v0.2.45"
|
||||||
|
|
||||||
// Agent metadata
|
// Agent metadata
|
||||||
#define DIDACTYL_NAME "Didactyl"
|
#define DIDACTYL_NAME "Didactyl"
|
||||||
|
|||||||
@@ -1535,11 +1535,13 @@ static void self_skill_cache_upsert_event_locked(cJSON* event) {
|
|||||||
|
|
||||||
int is_agent_author = (strcmp(pubkey->valuestring, g_cfg->keys.public_key_hex) == 0);
|
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);
|
int is_admin_author = (g_cfg->admin.pubkey[0] != '\0' && strcmp(pubkey->valuestring, g_cfg->admin.pubkey) == 0);
|
||||||
if (!is_agent_author && !is_admin_author) {
|
if (kind_val == 10123) {
|
||||||
return;
|
if (!is_agent_author && !is_admin_author) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_admin_author && kind_val != 31123) {
|
if (is_admin_author && kind_val == 31124) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3736,6 +3738,114 @@ int nostr_handler_refresh_self_skill_cache_from_relays(int timeout_ms) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cJSON* adopted_authors = cJSON_CreateObject();
|
||||||
|
if (adopted_authors) {
|
||||||
|
cJSON* latest_self_adoption = NULL;
|
||||||
|
double latest_self_adoption_created = -1.0;
|
||||||
|
int n0 = cJSON_GetArraySize(events);
|
||||||
|
for (int i = 0; i < n0; i++) {
|
||||||
|
cJSON* ev = cJSON_GetArrayItem(events, i);
|
||||||
|
if (!ev || !cJSON_IsObject(ev)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* kind = cJSON_GetObjectItemCaseSensitive(ev, "kind");
|
||||||
|
cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(ev, "pubkey");
|
||||||
|
if (!kind || !cJSON_IsNumber(kind) || (int)kind->valuedouble != 10123 ||
|
||||||
|
!pubkey || !cJSON_IsString(pubkey) || !pubkey->valuestring ||
|
||||||
|
strcmp(pubkey->valuestring, g_cfg->keys.public_key_hex) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* created_at = cJSON_GetObjectItemCaseSensitive(ev, "created_at");
|
||||||
|
double created = (created_at && cJSON_IsNumber(created_at)) ? created_at->valuedouble : 0.0;
|
||||||
|
if (!latest_self_adoption || created > latest_self_adoption_created) {
|
||||||
|
latest_self_adoption = ev;
|
||||||
|
latest_self_adoption_created = created;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latest_self_adoption) {
|
||||||
|
cJSON* tags = cJSON_GetObjectItemCaseSensitive(latest_self_adoption, "tags");
|
||||||
|
if (tags && cJSON_IsArray(tags)) {
|
||||||
|
int tn = cJSON_GetArraySize(tags);
|
||||||
|
for (int ti = 0; ti < tn; ti++) {
|
||||||
|
cJSON* tag = cJSON_GetArrayItem(tags, ti);
|
||||||
|
if (!tag || !cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cJSON* k = cJSON_GetArrayItem(tag, 0);
|
||||||
|
cJSON* v = cJSON_GetArrayItem(tag, 1);
|
||||||
|
if (!k || !v || !cJSON_IsString(k) || !k->valuestring ||
|
||||||
|
!cJSON_IsString(v) || !v->valuestring || strcmp(k->valuestring, "a") != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int adopted_kind = 0;
|
||||||
|
char adopted_pubkey[65] = {0};
|
||||||
|
char adopted_d_tag[65] = {0};
|
||||||
|
if (parse_skill_address_local(v->valuestring, &adopted_kind, adopted_pubkey, adopted_d_tag) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (adopted_pubkey[0] == '\0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!cJSON_GetObjectItemCaseSensitive(adopted_authors, adopted_pubkey)) {
|
||||||
|
cJSON_AddBoolToObject(adopted_authors, adopted_pubkey, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int adopted_author_count = 0;
|
||||||
|
cJSON* adopted_it = NULL;
|
||||||
|
cJSON_ArrayForEach(adopted_it, adopted_authors) {
|
||||||
|
adopted_author_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adopted_author_count > 0) {
|
||||||
|
cJSON* adopted_filter = cJSON_CreateObject();
|
||||||
|
cJSON* adopted_kinds = cJSON_CreateArray();
|
||||||
|
cJSON* adopted_author_arr = cJSON_CreateArray();
|
||||||
|
if (adopted_filter && adopted_kinds && adopted_author_arr) {
|
||||||
|
cJSON_AddItemToArray(adopted_kinds, cJSON_CreateNumber(31123));
|
||||||
|
cJSON_AddItemToArray(adopted_kinds, cJSON_CreateNumber(31124));
|
||||||
|
cJSON_AddItemToObject(adopted_filter, "kinds", adopted_kinds);
|
||||||
|
|
||||||
|
cJSON_ArrayForEach(adopted_it, adopted_authors) {
|
||||||
|
if (adopted_it->string && adopted_it->string[0] != '\0') {
|
||||||
|
cJSON_AddItemToArray(adopted_author_arr, cJSON_CreateString(adopted_it->string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(adopted_filter, "authors", adopted_author_arr);
|
||||||
|
cJSON_AddNumberToObject(adopted_filter, "limit", 1000);
|
||||||
|
|
||||||
|
char* adopted_json = nostr_handler_query_json(adopted_filter, effective_timeout_ms);
|
||||||
|
if (adopted_json) {
|
||||||
|
cJSON* adopted_events = cJSON_Parse(adopted_json);
|
||||||
|
free(adopted_json);
|
||||||
|
if (adopted_events && cJSON_IsArray(adopted_events)) {
|
||||||
|
int an = cJSON_GetArraySize(adopted_events);
|
||||||
|
for (int ai = 0; ai < an; ai++) {
|
||||||
|
cJSON* ev = cJSON_GetArrayItem(adopted_events, ai);
|
||||||
|
cJSON* dup = ev ? cJSON_Duplicate(ev, 1) : NULL;
|
||||||
|
if (dup) {
|
||||||
|
cJSON_AddItemToArray(events, dup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cJSON_Delete(adopted_events);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cJSON_Delete(adopted_filter);
|
||||||
|
cJSON_Delete(adopted_kinds);
|
||||||
|
cJSON_Delete(adopted_author_arr);
|
||||||
|
}
|
||||||
|
cJSON_Delete(adopted_filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cJSON_Delete(adopted_authors);
|
||||||
|
|
||||||
int merged = 0;
|
int merged = 0;
|
||||||
pthread_mutex_lock(&g_self_skill_mutex);
|
pthread_mutex_lock(&g_self_skill_mutex);
|
||||||
int n = cJSON_GetArraySize(events);
|
int n = cJSON_GetArraySize(events);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "tools_internal.h"
|
#include "tools_internal.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -131,6 +133,52 @@ static cJSON* parse_args_local(const char* args_json) {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int append_textf_skill_list_local(char** out, size_t* cap, size_t* used, const char* fmt, ...) {
|
||||||
|
if (!out || !cap || !used || !fmt) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
va_list ap2;
|
||||||
|
va_copy(ap2, ap);
|
||||||
|
int needed = vsnprintf(NULL, 0, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (needed < 0) {
|
||||||
|
va_end(ap2);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t need = (size_t)needed;
|
||||||
|
if (*used + need + 1U > *cap) {
|
||||||
|
size_t new_cap = (*cap > 0U) ? *cap : 256U;
|
||||||
|
while (*used + need + 1U > new_cap) {
|
||||||
|
if (new_cap > (SIZE_MAX / 2U)) {
|
||||||
|
va_end(ap2);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
new_cap *= 2U;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* grown = (char*)realloc(*out, new_cap);
|
||||||
|
if (!grown) {
|
||||||
|
va_end(ap2);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*out = grown;
|
||||||
|
*cap = new_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wrote = vsnprintf((*out) + (*used), *cap - *used, fmt, ap2);
|
||||||
|
va_end(ap2);
|
||||||
|
if (wrote < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*used += (size_t)wrote;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int is_hex_string_len_local(const char* s, size_t expected_len) {
|
static int is_hex_string_len_local(const char* s, size_t expected_len) {
|
||||||
if (!s || strlen(s) != expected_len) return 0;
|
if (!s || strlen(s) != expected_len) return 0;
|
||||||
for (size_t i = 0; i < expected_len; i++) {
|
for (size_t i = 0; i < expected_len; i++) {
|
||||||
@@ -1192,11 +1240,9 @@ char* execute_skill_list(tools_context_t* ctx, const char* args_json) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cJSON* out = cJSON_CreateObject();
|
cJSON* out = cJSON_CreateObject();
|
||||||
cJSON* summary_rows = cJSON_CreateArray();
|
|
||||||
cJSON* skills = cJSON_CreateArray();
|
cJSON* skills = cJSON_CreateArray();
|
||||||
if (!out || !summary_rows || !skills) {
|
if (!out || !skills) {
|
||||||
cJSON_Delete(out);
|
cJSON_Delete(out);
|
||||||
cJSON_Delete(summary_rows);
|
|
||||||
cJSON_Delete(skills);
|
cJSON_Delete(skills);
|
||||||
cJSON_Delete(events);
|
cJSON_Delete(events);
|
||||||
cJSON_Delete(adoption_tags);
|
cJSON_Delete(adoption_tags);
|
||||||
@@ -1204,7 +1250,21 @@ char* execute_skill_list(tools_context_t* ctx, const char* args_json) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t rendered_cap = 512U;
|
||||||
|
size_t rendered_used = 0U;
|
||||||
|
char* rendered_lines = (char*)malloc(rendered_cap);
|
||||||
|
if (!rendered_lines) {
|
||||||
|
cJSON_Delete(out);
|
||||||
|
cJSON_Delete(skills);
|
||||||
|
cJSON_Delete(events);
|
||||||
|
cJSON_Delete(adoption_tags);
|
||||||
|
free(adoption_content);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
rendered_lines[0] = '\0';
|
||||||
|
|
||||||
int adopted_count = 0;
|
int adopted_count = 0;
|
||||||
|
int skill_count = 0;
|
||||||
int event_count = cJSON_GetArraySize(events);
|
int event_count = cJSON_GetArraySize(events);
|
||||||
for (int i = 0; i < event_count; i++) {
|
for (int i = 0; i < event_count; i++) {
|
||||||
cJSON* ev = cJSON_GetArrayItem(events, i);
|
cJSON* ev = cJSON_GetArrayItem(events, i);
|
||||||
@@ -1236,59 +1296,91 @@ char* execute_skill_list(tools_context_t* ctx, const char* args_json) {
|
|||||||
if (adopted_filter >= 0 && is_adopted != adopted_filter) {
|
if (adopted_filter >= 0 && is_adopted != adopted_filter) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* owner = skill_owner_label_local(ctx->cfg, pubkey->valuestring);
|
||||||
|
cJSON* scope_tag = find_tag_value_string_local(tags, "scope");
|
||||||
|
cJSON* desc_tag = find_tag_value_string_local(tags, "description");
|
||||||
|
const char* scope_out = (scope_tag && cJSON_IsString(scope_tag) && scope_tag->valuestring)
|
||||||
|
? scope_tag->valuestring
|
||||||
|
: ((kind_val == 31124) ? "private" : "public");
|
||||||
|
const char* desc_out = (desc_tag && cJSON_IsString(desc_tag) && desc_tag->valuestring)
|
||||||
|
? desc_tag->valuestring
|
||||||
|
: "";
|
||||||
|
|
||||||
|
cJSON* row = cJSON_CreateObject();
|
||||||
|
if (!row) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cJSON_AddStringToObject(row, "d_tag", d->valuestring);
|
||||||
|
cJSON_AddStringToObject(row, "owner", owner);
|
||||||
|
cJSON_AddStringToObject(row, "scope", scope_out);
|
||||||
|
cJSON_AddNumberToObject(row, "kind", kind_val);
|
||||||
|
cJSON_AddBoolToObject(row, "adopted", is_adopted ? 1 : 0);
|
||||||
|
if (desc_out[0] != '\0') {
|
||||||
|
cJSON_AddStringToObject(row, "description", desc_out);
|
||||||
|
}
|
||||||
|
cJSON_AddItemToArray(skills, row);
|
||||||
|
|
||||||
if (is_adopted) {
|
if (is_adopted) {
|
||||||
adopted_count++;
|
adopted_count++;
|
||||||
}
|
}
|
||||||
|
skill_count++;
|
||||||
|
|
||||||
const char* owner = skill_owner_label_local(ctx->cfg, pubkey->valuestring);
|
if (append_textf_skill_list_local(&rendered_lines,
|
||||||
|
&rendered_cap,
|
||||||
cJSON* detail = extract_skill_summary_local(ev);
|
&rendered_used,
|
||||||
if (detail) {
|
"- %s | owner=%s | scope=%s | adopted=%s%s%s\n",
|
||||||
cJSON_AddStringToObject(detail, "owner", owner);
|
d->valuestring,
|
||||||
cJSON_AddBoolToObject(detail, "adopted", is_adopted ? 1 : 0);
|
owner,
|
||||||
cJSON_AddItemToArray(skills, detail);
|
scope_out,
|
||||||
}
|
is_adopted ? "yes" : "no",
|
||||||
|
desc_out[0] ? " | description=" : "",
|
||||||
cJSON* row = cJSON_CreateObject();
|
desc_out) != 0) {
|
||||||
if (row) {
|
free(rendered_lines);
|
||||||
cJSON_AddStringToObject(row, "d_tag", d->valuestring);
|
cJSON_Delete(out);
|
||||||
cJSON_AddStringToObject(row, "owner", owner);
|
cJSON_Delete(skills);
|
||||||
cJSON_AddBoolToObject(row, "adopted", is_adopted ? 1 : 0);
|
cJSON_Delete(events);
|
||||||
cJSON_AddItemToArray(summary_rows, row);
|
cJSON_Delete(adoption_tags);
|
||||||
|
free(adoption_content);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON* skills_pretty = cJSON_CreateArray();
|
if (skill_count == 0) {
|
||||||
if (!skills_pretty) {
|
if (append_textf_skill_list_local(&rendered_lines, &rendered_cap, &rendered_used, "- (none)\n") != 0) {
|
||||||
|
free(rendered_lines);
|
||||||
|
cJSON_Delete(out);
|
||||||
|
cJSON_Delete(skills);
|
||||||
|
cJSON_Delete(events);
|
||||||
|
cJSON_Delete(adoption_tags);
|
||||||
|
free(adoption_content);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char header[128];
|
||||||
|
snprintf(header, sizeof(header), "Skills: %d total (%d adopted)\n", skill_count, adopted_count);
|
||||||
|
size_t header_len = strlen(header);
|
||||||
|
char* rendered = (char*)malloc(header_len + rendered_used + 1U);
|
||||||
|
if (!rendered) {
|
||||||
|
free(rendered_lines);
|
||||||
cJSON_Delete(out);
|
cJSON_Delete(out);
|
||||||
|
cJSON_Delete(skills);
|
||||||
cJSON_Delete(events);
|
cJSON_Delete(events);
|
||||||
cJSON_Delete(adoption_tags);
|
cJSON_Delete(adoption_tags);
|
||||||
free(adoption_content);
|
free(adoption_content);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
memcpy(rendered, header, header_len);
|
||||||
int skill_count = cJSON_GetArraySize(skills);
|
memcpy(rendered + header_len, rendered_lines, rendered_used + 1U);
|
||||||
for (int i = 0; i < skill_count; i++) {
|
free(rendered_lines);
|
||||||
cJSON* skill_item = cJSON_GetArrayItem(skills, i);
|
|
||||||
if (!skill_item) continue;
|
|
||||||
char* skill_json = cJSON_PrintUnformatted(skill_item);
|
|
||||||
if (!skill_json) continue;
|
|
||||||
|
|
||||||
cJSON* block = cJSON_CreateString(skill_json);
|
|
||||||
free(skill_json);
|
|
||||||
if (block) {
|
|
||||||
cJSON_AddItemToArray(skills_pretty, block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON_AddBoolToObject(out, "success", 1);
|
cJSON_AddBoolToObject(out, "success", 1);
|
||||||
cJSON_AddBoolToObject(out, "fallback_used", 0);
|
cJSON_AddNumberToObject(out, "count", skill_count);
|
||||||
cJSON_AddStringToObject(out, "skills_separator", "\n\n\n");
|
|
||||||
cJSON_AddItemToObject(out, "summary", summary_rows);
|
|
||||||
cJSON_AddItemToObject(out, "skills", skills);
|
|
||||||
cJSON_AddItemToObject(out, "skills_pretty", skills_pretty);
|
|
||||||
cJSON_AddNumberToObject(out, "count", cJSON_GetArraySize(skills));
|
|
||||||
cJSON_AddNumberToObject(out, "adopted_count", adopted_count);
|
cJSON_AddNumberToObject(out, "adopted_count", adopted_count);
|
||||||
|
cJSON_AddItemToObject(out, "skills", skills);
|
||||||
|
cJSON_AddStringToObject(out, "rendered", rendered);
|
||||||
|
free(rendered);
|
||||||
|
|
||||||
char* json = cJSON_PrintUnformatted(out);
|
char* json = cJSON_PrintUnformatted(out);
|
||||||
cJSON_Delete(out);
|
cJSON_Delete(out);
|
||||||
|
|||||||
Reference in New Issue
Block a user