diff --git a/README.md b/README.md index fc79d04..f78d1cb 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.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 diff --git a/context.log.md b/context.log.md index cf508d3..efb15bc 100644 --- a/context.log.md +++ b/context.log.md @@ -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 diff --git a/src/agent.c b/src/agent.c index f27beb1..91104ce 100644 --- a/src/agent.c +++ b/src/agent.c @@ -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."; diff --git a/src/main.h b/src/main.h index 15e4901..335c363 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 5 -#define DIDACTYL_VERSION "v0.2.5" +#define DIDACTYL_VERSION_PATCH 6 +#define DIDACTYL_VERSION "v0.2.6" // Agent metadata #define DIDACTYL_NAME "Didactyl"