v0.2.6 - Fix DM freeze by removing adopted-skills mutex reentrancy in trigger context assembly

This commit is contained in:
Your Name
2026-03-22 12:28:54 -04:00
parent b625e0a70a
commit b06c267db4
4 changed files with 229 additions and 9 deletions

View File

@@ -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.5
## Current Status — v0.2.6
**Active build — this project is barely working. Experiment at your own risk.**
> Last release update: v0.2.5 — Fix increment_and_push upstream auto-setup and finalize SIGINT shutdown fix
> Last release update: v0.2.6 — Fix DM freeze by removing adopted-skills mutex reentrancy in trigger context assembly
- Connects to configured relays with auto-reconnect and relay state transition logging
- Publishes configured startup events per relay as each relay becomes connected

View File

@@ -1,3 +1,209 @@
```text
Context Log - not seen by model
timestamp=2026-03-22 12:19:28
phase=direct_tool_exec
sender=1ec454734dcbf6fe54901ce25c0c7c6bca5edd89443416761fadc321d38df139
model=claude-opus-4.6
context_bytes=13723
approx_tokens=3430
```
slash=/help
result_json=AVAILABLE TOOLS
- admin_identity — Build admin identity context block from cached runtime metadata
- agent_identity — Build agent identity context block with pubkey and npub
- agent_version — Return current Didactyl version and metadata from build macros
- blossom_delete — Delete a blob from Blossom
- blossom_download — Download a blob from Blossom to a local file
- blossom_head — Fetch blob metadata from Blossom
- blossom_list — List blobs for a pubkey on Blossom
- blossom_upload — Upload a local file to a Blossom server
- cashu_wallet_balance — Return current wallet balances aggregated by mint and unit
- cashu_wallet_check_proofs — Check current wallet proofs state against mint
- cashu_wallet_info — Fetch mint info for a specific mint_url or the default configured mint
- cashu_wallet_melt_pay — Pay a previously quoted melt by quote_id
- cashu_wallet_melt_quote — Request a melt quote for a Lightning payment request
- cashu_wallet_mint_check — Check a previously requested mint quote status
- cashu_wallet_mint_claim — Claim minted proofs for a paid quote_id and add them to wallet state
- cashu_wallet_mint_quote — Request a mint quote for a target amount
- cashu_wallet_mints_get — Get currently configured wallet and public mints
- cashu_wallet_mints_set — Set wallet mints (NIP-60), public mints (NIP-61), or both
- cashu_wallet_receive_token — Receive an ecash token string (cashuA/cashuB), swap to fresh proofs, and store it in wallet state
- cashu_wallet_send_token — Create an outbound ecash token from wallet proofs for an exact amount
- config_recall — Fetch and decrypt agent config kind 30078 by d_tag
- config_store — Encrypt and publish agent config as kind 30078 for a given d_tag
- local_file_read — Read a local file as text from the configured working directory
- local_file_write — Write text content to a local file in the configured working directory
- local_http_fetch — Fetch HTTP(S) resources with optional method, headers, timeout, and body
- local_shell_exec — Execute a shell command and return stdout/stderr
- memory_recall — Recall encrypted agent memory (kind 30078, d=memory)
- memory_save — Prepend a new entry to encrypted agent memory (kind 30078, d=memory) and truncate oldest content if needed
- model_get — Get current active LLM runtime configuration (excluding API key)
- model_list — List available model IDs using provider OpenAI-compatible /models endpoint
- model_set — Update active LLM configuration and persist it to Nostr kind 30078 (d=llm_config)
- my_contacts — Alias for nostr_agent_contacts: return this agent's kind 3 contacts context
- my_kind0_profile — Alias for nostr_agent_profile: return this agent's kind 0 profile context
- my_notes — Alias for nostr_agent_notes: return this agent's recent kind 1 notes context
- my_npub — Alias for nostr_npub: return this agent's pubkey encoded as npub bech32
- my_pubkey — Alias for nostr_pubkey: return this agent's pubkey in hex format
- my_relays — Alias for nostr_agent_relays: return this agent's kind 10002 relay context
- nostr_admin_contacts — Build admin contacts context block from cached kind 3 contact list
- nostr_admin_notes — Build admin notes context block from cached kind 1 notes
- nostr_admin_profile — Build admin profile context block from cached kind 0 metadata
- nostr_admin_relays — Build admin relay context block from cached kind 10002 data
- nostr_agent_contacts — Build agent contacts context block from cached kind 3 contact list
- nostr_agent_notes — Build agent notes context block from cached kind 1 notes
- nostr_agent_profile — Build agent profile context block from cached kind 0 metadata
- nostr_agent_relays — Build agent relay context block from cached kind 10002 data
- nostr_block_edit — Add or remove blocked tuples in kind-10000 block list; entries are private (encrypted) by default
- nostr_block_list — View blocked pubkeys, event IDs, or hashtags from the local encrypted kind-10000 block list cache
- nostr_decode — Decode a Nostr bech32/nostr: URI into components
- nostr_decrypt — Decrypt NIP-44 ciphertext from a sender
- nostr_delete — Request deletion of one or more previously published events (NIP-09 kind 5)
- nostr_dm_send — Send a NIP-04 encrypted DM
- nostr_dm_send_nip17 — Send a private DM using NIP-17 gift wrap protocol
- nostr_encode — Encode a Nostr entity into nostr: URI (npub, note, nprofile, nevent, naddr)
- nostr_encrypt — Encrypt plaintext using NIP-44 for a recipient
- nostr_file_md_to_longform_post — Read a markdown file and publish it as kind 30023 longform post; defaults d-tag to lowercase filename
- nostr_list_manage — Add/remove tag tuples in replaceable list events (NIP-51 style)
- nostr_my_events — Query recent events authored by this agent and return kind, event_id, timestamp, d_tag, and cache presence
- nostr_nip05_lookup — Look up or verify a NIP-05 identifier (user@domain)
- nostr_npub — Return this agent's pubkey encoded as npub bech32
- nostr_post — Publish a Nostr event to connected relays
- nostr_post_readme — Publish README.md as kind 30023 with deterministic d tag readme.md
- nostr_profile_get — Look up a Nostr profile (kind 0 metadata) by pubkey
- nostr_pubkey — Return this agent's pubkey in hex format
- nostr_query — Query events from relays using a Nostr filter
- nostr_react — React to a Nostr event with like/dislike/emoji (NIP-25 kind 7)
- nostr_relay_info — Fetch NIP-11 relay information document
- nostr_relay_status — Get connection status and statistics for all relays
- nostr_subscription_set — Update one managed runtime subscription by name (toggle enabled and/or replace filter)
- nostr_subscription_status — List currently managed runtime Nostr subscriptions and filters
- skill_adopt — Adopt a skill by adding its address to kind 10123 adoption list
- skill_create — Create or update a skill definition as kind 31123/31124 and optionally auto-adopt it
- skill_edit — Edit an existing self skill by d tag and republish it as kind 31123/31124
- skill_list — List available skills discovered online (agent + admin), with adoption status and optional filters
- skill_remove — Remove a skill address from kind 10123 adoption list
- skill_search — Search public skills by query/author and optionally rank by adoption popularity
- task_list — Build current task list context block from agent task memory on Nostr
- task_manage — Manage agent short-term task memory stored on Nostr kind 30078 (d=tasks): list/add/update/remove/clear/replace
- tool_list — List available tools with name, description, and JSON parameter schema
- trigger_list — List active triggered skills and their runtime status
AVAILABLE SKILLS
- (none)
ADOPTED SKILLS
- default_admin_dm
result_markdown=AVAILABLE TOOLS
- admin_identity — Build admin identity context block from cached runtime metadata
- agent_identity — Build agent identity context block with pubkey and npub
- agent_version — Return current Didactyl version and metadata from build macros
- blossom_delete — Delete a blob from Blossom
- blossom_download — Download a blob from Blossom to a local file
- blossom_head — Fetch blob metadata from Blossom
- blossom_list — List blobs for a pubkey on Blossom
- blossom_upload — Upload a local file to a Blossom server
- cashu_wallet_balance — Return current wallet balances aggregated by mint and unit
- cashu_wallet_check_proofs — Check current wallet proofs state against mint
- cashu_wallet_info — Fetch mint info for a specific mint_url or the default configured mint
- cashu_wallet_melt_pay — Pay a previously quoted melt by quote_id
- cashu_wallet_melt_quote — Request a melt quote for a Lightning payment request
- cashu_wallet_mint_check — Check a previously requested mint quote status
- cashu_wallet_mint_claim — Claim minted proofs for a paid quote_id and add them to wallet state
- cashu_wallet_mint_quote — Request a mint quote for a target amount
- cashu_wallet_mints_get — Get currently configured wallet and public mints
- cashu_wallet_mints_set — Set wallet mints (NIP-60), public mints (NIP-61), or both
- cashu_wallet_receive_token — Receive an ecash token string (cashuA/cashuB), swap to fresh proofs, and store it in wallet state
- cashu_wallet_send_token — Create an outbound ecash token from wallet proofs for an exact amount
- config_recall — Fetch and decrypt agent config kind 30078 by d_tag
- config_store — Encrypt and publish agent config as kind 30078 for a given d_tag
- local_file_read — Read a local file as text from the configured working directory
- local_file_write — Write text content to a local file in the configured working directory
- local_http_fetch — Fetch HTTP(S) resources with optional method, headers, timeout, and body
- local_shell_exec — Execute a shell command and return stdout/stderr
- memory_recall — Recall encrypted agent memory (kind 30078, d=memory)
- memory_save — Prepend a new entry to encrypted agent memory (kind 30078, d=memory) and truncate oldest content if needed
- model_get — Get current active LLM runtime configuration (excluding API key)
- model_list — List available model IDs using provider OpenAI-compatible /models endpoint
- model_set — Update active LLM configuration and persist it to Nostr kind 30078 (d=llm_config)
- my_contacts — Alias for nostr_agent_contacts: return this agent's kind 3 contacts context
- my_kind0_profile — Alias for nostr_agent_profile: return this agent's kind 0 profile context
- my_notes — Alias for nostr_agent_notes: return this agent's recent kind 1 notes context
- my_npub — Alias for nostr_npub: return this agent's pubkey encoded as npub bech32
- my_pubkey — Alias for nostr_pubkey: return this agent's pubkey in hex format
- my_relays — Alias for nostr_agent_relays: return this agent's kind 10002 relay context
- nostr_admin_contacts — Build admin contacts context block from cached kind 3 contact list
- nostr_admin_notes — Build admin notes context block from cached kind 1 notes
- nostr_admin_profile — Build admin profile context block from cached kind 0 metadata
- nostr_admin_relays — Build admin relay context block from cached kind 10002 data
- nostr_agent_contacts — Build agent contacts context block from cached kind 3 contact list
- nostr_agent_notes — Build agent notes context block from cached kind 1 notes
- nostr_agent_profile — Build agent profile context block from cached kind 0 metadata
- nostr_agent_relays — Build agent relay context block from cached kind 10002 data
- nostr_block_edit — Add or remove blocked tuples in kind-10000 block list; entries are private (encrypted) by default
- nostr_block_list — View blocked pubkeys, event IDs, or hashtags from the local encrypted kind-10000 block list cache
- nostr_decode — Decode a Nostr bech32/nostr: URI into components
- nostr_decrypt — Decrypt NIP-44 ciphertext from a sender
- nostr_delete — Request deletion of one or more previously published events (NIP-09 kind 5)
- nostr_dm_send — Send a NIP-04 encrypted DM
- nostr_dm_send_nip17 — Send a private DM using NIP-17 gift wrap protocol
- nostr_encode — Encode a Nostr entity into nostr: URI (npub, note, nprofile, nevent, naddr)
- nostr_encrypt — Encrypt plaintext using NIP-44 for a recipient
- nostr_file_md_to_longform_post — Read a markdown file and publish it as kind 30023 longform post; defaults d-tag to lowercase filename
- nostr_list_manage — Add/remove tag tuples in replaceable list events (NIP-51 style)
- nostr_my_events — Query recent events authored by this agent and return kind, event_id, timestamp, d_tag, and cache presence
- nostr_nip05_lookup — Look up or verify a NIP-05 identifier (user@domain)
- nostr_npub — Return this agent's pubkey encoded as npub bech32
- nostr_post — Publish a Nostr event to connected relays
- nostr_post_readme — Publish README.md as kind 30023 with deterministic d tag readme.md
- nostr_profile_get — Look up a Nostr profile (kind 0 metadata) by pubkey
- nostr_pubkey — Return this agent's pubkey in hex format
- nostr_query — Query events from relays using a Nostr filter
- nostr_react — React to a Nostr event with like/dislike/emoji (NIP-25 kind 7)
- nostr_relay_info — Fetch NIP-11 relay information document
- nostr_relay_status — Get connection status and statistics for all relays
- nostr_subscription_set — Update one managed runtime subscription by name (toggle enabled and/or replace filter)
- nostr_subscription_status — List currently managed runtime Nostr subscriptions and filters
- skill_adopt — Adopt a skill by adding its address to kind 10123 adoption list
- skill_create — Create or update a skill definition as kind 31123/31124 and optionally auto-adopt it
- skill_edit — Edit an existing self skill by d tag and republish it as kind 31123/31124
- skill_list — List available skills discovered online (agent + admin), with adoption status and optional filters
- skill_remove — Remove a skill address from kind 10123 adoption list
- skill_search — Search public skills by query/author and optionally rank by adoption popularity
- task_list — Build current task list context block from agent task memory on Nostr
- task_manage — Manage agent short-term task memory stored on Nostr kind 30078 (d=tasks): list/add/update/remove/clear/replace
- tool_list — List available tools with name, description, and JSON parameter schema
- trigger_list — List active triggered skills and their runtime status
AVAILABLE SKILLS
- (none)
ADOPTED SKILLS
- default_admin_dm
---
```text
Context Log - not seen by model
timestamp=2026-03-22 12:19:16
phase=direct_tool_exec
sender=1ec454734dcbf6fe54901ce25c0c7c6bca5edd89443416761fadc321d38df139
model=claude-opus-4.6
context_bytes=129
approx_tokens=32
```
slash=/tools
result_json={"success":false,"error":"unknown tool"}
result_markdown=- **success:** false
- **error:** unknown tool
---
```text
Context Log - not seen by model
timestamp=2026-03-22 12:11:32

View File

@@ -1596,8 +1596,12 @@ static char* build_context_from_triggers(trigger_type_t trigger_type,
}
}
char* matched_skill_contents[AGENT_ADOPTED_SKILLS_MAX];
int matched_skill_count = 0;
memset(matched_skill_contents, 0, sizeof(matched_skill_contents));
pthread_mutex_lock(&g_adopted_skills_mutex);
for (int i = 0; i < g_adopted_skills_count; i++) {
for (int i = 0; i < g_adopted_skills_count && matched_skill_count < AGENT_ADOPTED_SKILLS_MAX; i++) {
const agent_adopted_skill_t* s = &g_adopted_skills[i];
if (!s->has_trigger || s->trigger_type != trigger_type) {
continue;
@@ -1606,8 +1610,16 @@ static char* build_context_from_triggers(trigger_type_t trigger_type,
continue;
}
char* expanded = resolve_skill_references_local(s->content ? s->content : "");
const char* skill_text = expanded ? expanded : (s->content ? s->content : "");
matched_skill_contents[matched_skill_count] = strdup(s->content ? s->content : "");
if (matched_skill_contents[matched_skill_count]) {
matched_skill_count++;
}
}
pthread_mutex_unlock(&g_adopted_skills_mutex);
for (int i = 0; i < matched_skill_count; i++) {
char* expanded = resolve_skill_references_local(matched_skill_contents[i] ? matched_skill_contents[i] : "");
const char* skill_text = expanded ? expanded : (matched_skill_contents[i] ? matched_skill_contents[i] : "");
size_t need = strlen("\n\n---\n\n") + strlen(skill_text) + 1U;
if (used + need >= cap) {
@@ -1616,7 +1628,9 @@ static char* build_context_from_triggers(trigger_type_t trigger_type,
char* grown = (char*)realloc(out, next);
if (!grown) {
free(expanded);
pthread_mutex_unlock(&g_adopted_skills_mutex);
for (int j = i; j < matched_skill_count; j++) {
free(matched_skill_contents[j]);
}
free(out);
return NULL;
}
@@ -1635,8 +1649,8 @@ static char* build_context_from_triggers(trigger_type_t trigger_type,
matched++;
free(expanded);
free(matched_skill_contents[i]);
}
pthread_mutex_unlock(&g_adopted_skills_mutex);
if (matched == 0) {
const char* fallback = "You are an AI agent. Respond to the message.";

View File

@@ -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 5
#define DIDACTYL_VERSION "v0.2.5"
#define DIDACTYL_VERSION_PATCH 6
#define DIDACTYL_VERSION "v0.2.6"
// Agent metadata
#define DIDACTYL_NAME "Didactyl"