v0.0.65 - Filter skill_list by 10123 adoption tags, gate live trigger registration on adoption, and allow underscores in skill d-tags
This commit is contained in:
@@ -55,11 +55,11 @@ Skills support context modes (`inject`, `full`, `override`) and per-skill LLM fa
|
||||
|
||||
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.64
|
||||
## Current Status — v0.0.65
|
||||
|
||||
**Active build — this project is barely working. Experiment at your own risk.**
|
||||
|
||||
> Last release update: v0.0.64 — Just keeping things fresh
|
||||
> Last release update: v0.0.65 — Filter skill_list by 10123 adoption tags, gate live trigger registration on adoption, and allow underscores in skill d-tags
|
||||
|
||||
- Connects to configured relays with auto-reconnect and relay state transition logging
|
||||
- Publishes configured startup events per relay as each relay becomes connected
|
||||
|
||||
@@ -246,6 +246,57 @@
|
||||
]
|
||||
},
|
||||
|
||||
// Kind 31124: Private triggered skill — webhook-echo
|
||||
// Example webhook trigger that emits a fixed DM response.
|
||||
{
|
||||
"kind": 31124,
|
||||
"content": "When this webhook trigger fires, DM the admin with exactly: WEBHOOK_SOURCE_POC_OK",
|
||||
"tags": [
|
||||
["d", "webhook-echo"],
|
||||
["app", "didactyl"],
|
||||
["scope", "private"],
|
||||
["description", "PoC webhook trigger skill"],
|
||||
["trigger", "webhook"],
|
||||
["filter", "{}"],
|
||||
["action", "llm"],
|
||||
["enabled", "true"]
|
||||
]
|
||||
},
|
||||
|
||||
// Kind 31124: Private triggered skill — cron_poc
|
||||
// Example cron trigger that runs every minute.
|
||||
{
|
||||
"kind": 31124,
|
||||
"content": "DM the administrator with the current time, and tell him that your cron skill triggered.",
|
||||
"tags": [
|
||||
["d", "cron_poc"],
|
||||
["app", "didactyl"],
|
||||
["scope", "private"],
|
||||
["description", "PoC cron trigger skill"],
|
||||
["trigger", "cron"],
|
||||
["filter", "*/1 * * * *"],
|
||||
["action", "template"],
|
||||
["enabled", "true"]
|
||||
]
|
||||
},
|
||||
|
||||
// Kind 31124: Private triggered skill — chain_from_webhook_poc
|
||||
// Example chain trigger that runs after webhook-echo completes.
|
||||
{
|
||||
"kind": 31124,
|
||||
"content": "LOG: chain_from_webhook_poc fired",
|
||||
"tags": [
|
||||
["d", "chain_from_webhook_poc"],
|
||||
["app", "didactyl"],
|
||||
["scope", "private"],
|
||||
["description", "PoC chain trigger skill"],
|
||||
["trigger", "chain"],
|
||||
["filter", "webhook-echo"],
|
||||
["action", "template"],
|
||||
["enabled", "true"]
|
||||
]
|
||||
},
|
||||
|
||||
// Kind 10123: Skill adoption list
|
||||
// References which public skills this agent has adopted.
|
||||
{
|
||||
|
||||
+2
-2
@@ -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 64
|
||||
#define DIDACTYL_VERSION "v0.0.64"
|
||||
#define DIDACTYL_VERSION_PATCH 65
|
||||
#define DIDACTYL_VERSION "v0.0.65"
|
||||
|
||||
// Agent metadata
|
||||
#define DIDACTYL_NAME "Didactyl"
|
||||
|
||||
@@ -456,6 +456,58 @@ static int parse_enabled_flag_local(const char* enabled_s) {
|
||||
return (strcmp(enabled_s, "false") == 0 || strcmp(enabled_s, "0") == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int self_skill_is_adopted_local(int kind, const char* pubkey_hex, const char* d_tag) {
|
||||
if ((kind != 31123 && kind != 31124) || !pubkey_hex || !d_tag || d_tag[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char addr[256];
|
||||
snprintf(addr, sizeof(addr), "%d:%s:%s", kind, pubkey_hex, d_tag);
|
||||
|
||||
int adopted = 0;
|
||||
pthread_mutex_lock(&g_self_skill_mutex);
|
||||
|
||||
if (g_self_skill_events && cJSON_IsArray(g_self_skill_events)) {
|
||||
int n = cJSON_GetArraySize(g_self_skill_events);
|
||||
for (int i = 0; i < n && !adopted; i++) {
|
||||
cJSON* ev = cJSON_GetArrayItem(g_self_skill_events, i);
|
||||
cJSON* ev_kind = ev ? cJSON_GetObjectItemCaseSensitive(ev, "kind") : NULL;
|
||||
cJSON* ev_pubkey = ev ? cJSON_GetObjectItemCaseSensitive(ev, "pubkey") : NULL;
|
||||
cJSON* ev_tags = ev ? cJSON_GetObjectItemCaseSensitive(ev, "tags") : NULL;
|
||||
|
||||
if (!ev_kind || !cJSON_IsNumber(ev_kind) || (int)ev_kind->valuedouble != 10123 ||
|
||||
!ev_pubkey || !cJSON_IsString(ev_pubkey) || !ev_pubkey->valuestring ||
|
||||
strcmp(ev_pubkey->valuestring, pubkey_hex) != 0 ||
|
||||
!ev_tags || !cJSON_IsArray(ev_tags)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int tag_count = cJSON_GetArraySize(ev_tags);
|
||||
for (int ti = 0; ti < tag_count; ti++) {
|
||||
cJSON* tag = cJSON_GetArrayItem(ev_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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(k->valuestring, "a") == 0 && strcmp(v->valuestring, addr) == 0) {
|
||||
adopted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_self_skill_mutex);
|
||||
return adopted;
|
||||
}
|
||||
|
||||
static void register_trigger_from_self_skill_event(cJSON* event) {
|
||||
if (!event || !g_cfg || !g_trigger_manager) {
|
||||
return;
|
||||
@@ -499,6 +551,11 @@ static void register_trigger_from_self_skill_event(cJSON* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self_skill_is_adopted_local(kind_val, pubkey->valuestring, d_tag)) {
|
||||
DEBUG_INFO("[didactyl] live self-skill trigger ignored (not adopted) d_tag=%s", d_tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action && strcmp(action, "template") == 0) {
|
||||
DEBUG_WARN("[didactyl] live self-skill trigger action template is deprecated; forcing llm for d_tag=%s", d_tag);
|
||||
}
|
||||
|
||||
+64
-3
@@ -885,7 +885,7 @@ static int validate_skill_d_tag(const char* d_tag) {
|
||||
prev_dash = 1;
|
||||
continue;
|
||||
}
|
||||
if (!islower(c) && !isdigit(c)) return 0;
|
||||
if (!islower(c) && !isdigit(c) && c != '_') return 0;
|
||||
prev_dash = 0;
|
||||
}
|
||||
|
||||
@@ -978,6 +978,37 @@ static int publish_adoption_list(const char* content, cJSON* tags, nostr_publish
|
||||
return nostr_handler_publish_kind_event(10123, content ? content : "", tags, out_result);
|
||||
}
|
||||
|
||||
static int adoption_tags_contains_address(cJSON* adoption_tags,
|
||||
int kind,
|
||||
const char* pubkey,
|
||||
const char* d_tag) {
|
||||
if (!adoption_tags || !cJSON_IsArray(adoption_tags) || !pubkey || !d_tag || d_tag[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char addr[256];
|
||||
snprintf(addr, sizeof(addr), "%d:%s:%s", kind, pubkey, d_tag);
|
||||
|
||||
int tag_count = cJSON_GetArraySize(adoption_tags);
|
||||
for (int i = 0; i < tag_count; i++) {
|
||||
cJSON* tag = cJSON_GetArrayItem(adoption_tags, i);
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(k->valuestring, "a") == 0 && strcmp(v->valuestring, addr) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cJSON* extract_skill_summary(cJSON* event) {
|
||||
if (!event || !cJSON_IsObject(event)) return NULL;
|
||||
|
||||
@@ -3751,13 +3782,25 @@ static char* execute_skill_list(tools_context_t* ctx, const char* args_json) {
|
||||
}
|
||||
cJSON_Delete(args);
|
||||
|
||||
cJSON* adoption_tags = NULL;
|
||||
char* adoption_content = NULL;
|
||||
if (fetch_adoption_list_tags(ctx, &adoption_tags, &adoption_content) != 0) {
|
||||
return json_error("skill_list failed to load adoption list");
|
||||
}
|
||||
|
||||
char* events_json = nostr_handler_get_self_skill_events_json();
|
||||
if (!events_json) return json_error("skill_list cache unavailable");
|
||||
if (!events_json) {
|
||||
cJSON_Delete(adoption_tags);
|
||||
free(adoption_content);
|
||||
return json_error("skill_list cache unavailable");
|
||||
}
|
||||
|
||||
cJSON* events = cJSON_Parse(events_json);
|
||||
free(events_json);
|
||||
if (!events || !cJSON_IsArray(events)) {
|
||||
cJSON_Delete(events);
|
||||
cJSON_Delete(adoption_tags);
|
||||
free(adoption_content);
|
||||
return json_error("skill_list cache returned invalid JSON");
|
||||
}
|
||||
|
||||
@@ -3767,6 +3810,8 @@ static char* execute_skill_list(tools_context_t* ctx, const char* args_json) {
|
||||
cJSON_Delete(out);
|
||||
cJSON_Delete(skills);
|
||||
cJSON_Delete(events);
|
||||
cJSON_Delete(adoption_tags);
|
||||
free(adoption_content);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -3776,14 +3821,28 @@ static char* execute_skill_list(tools_context_t* ctx, const char* args_json) {
|
||||
if (!ev || !cJSON_IsObject(ev)) continue;
|
||||
|
||||
cJSON* kind = cJSON_GetObjectItemCaseSensitive(ev, "kind");
|
||||
if (!kind || !cJSON_IsNumber(kind)) {
|
||||
cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(ev, "pubkey");
|
||||
cJSON* tags = cJSON_GetObjectItemCaseSensitive(ev, "tags");
|
||||
if (!kind || !cJSON_IsNumber(kind) ||
|
||||
!pubkey || !cJSON_IsString(pubkey) || !pubkey->valuestring ||
|
||||
!tags || !cJSON_IsArray(tags)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int kind_val = (int)kind->valuedouble;
|
||||
if ((kind_val == 31123 && !include_public) || (kind_val == 31124 && !include_private)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cJSON* d = find_tag_value_string(tags, "d");
|
||||
if (!d || !cJSON_IsString(d) || !d->valuestring || d->valuestring[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!adoption_tags_contains_address(adoption_tags, kind_val, pubkey->valuestring, d->valuestring)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cJSON* summary = extract_skill_summary(ev);
|
||||
if (summary) cJSON_AddItemToArray(skills, summary);
|
||||
}
|
||||
@@ -3796,6 +3855,8 @@ static char* execute_skill_list(tools_context_t* ctx, const char* args_json) {
|
||||
char* json = cJSON_PrintUnformatted(out);
|
||||
cJSON_Delete(out);
|
||||
cJSON_Delete(events);
|
||||
cJSON_Delete(adoption_tags);
|
||||
free(adoption_content);
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user