v0.2.28 - Updated LT and Anvil genesis configs: unified DM history context as startup skill, migrated default skills into startup_events, rotated admin npub to npub1rmz9..., and redeployed/restarted didactyl+simon+anvil

This commit is contained in:
Didactyl User
2026-04-07 06:33:15 -04:00
parent 132dfb77c7
commit 8d384721bf
11 changed files with 300 additions and 25 deletions
+2 -2
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. 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.27 ## Current Status — v0.2.28
**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.27 — Remove markdown context snapshot logging; keep only raw provider request JSON logging and simplify context debug plumbing > Last release update: v0.2.28 — Updated LT and Anvil genesis configs: unified DM history context as startup skill, migrated default skills into startup_events, rotated admin npub to npub1rmz9..., and redeployed/restarted didactyl+simon+anvil
- 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
+4 -4
View File
@@ -24,7 +24,7 @@
{ {
"kind": 30078, "kind": 30078,
"d_tag": "user-settings", "d_tag": "user-settings",
"content": "{\"v\":2,\"updatedAt\":0,\"global_llm\":{\"provider\":\"openai\",\"api_key\":\"sk-REPLACE_WITH_API_KEY\",\"model\":\"gpt-4o-mini\",\"base_url\":\"https://api.openai.com/v1\",\"max_tokens\":512,\"temperature\":0.7},\"didactyl\":{\"admin_pubkey\":\"npub1REPLACE_WITH_ADMIN_PUBKEY\",\"dm_protocol\":\"nip04\"}}" "content": "{\"v\":2,\"updatedAt\":0,\"global_llm\":{\"provider\":\"openai\",\"api_key\":\"sk-REPLACE_WITH_API_KEY\",\"model\":\"gpt-4o-mini\",\"base_url\":\"https://api.openai.com/v1\",\"max_tokens\":512,\"temperature\":0.7},\"didactyl\":{\"admin_pubkey\":\"npub1REPLACE_WITH_ADMIN_PUBKEY\",\"dm_protocol\":\"nip04\",\"max_turns\":40}}"
} }
], ],
@@ -74,12 +74,12 @@
}, },
{ {
"kind": 31124, "kind": 31124,
"content": "## Recent Conversation\n\n{{nostr_dm_history({\"format\":\"text\",\"limit\":12})}}", "content": "## DM History Context\n\n### Instructions\n- Reference prior conversation naturally when it's relevant to the current request.\n- Do not repeat entire DM history back to the user unless explicitly asked.\n- Use this context to avoid asking questions that were already answered in recent messages.\n\n### Recent DM History (last 10 messages)\n\n{{nostr_dm_history({\"limit\":10,\"format\":\"text\"})}}",
"tags": [ "tags": [
["d", "dm_history"], ["d", "dm_history_context"],
["app", "didactyl"], ["app", "didactyl"],
["scope", "private"], ["scope", "private"],
["description", "DM conversation history for context continuity"], ["description", "DM history context"],
["trigger", "dm"], ["trigger", "dm"],
["filter", "{\"from\":\"admin\"}"] ["filter", "{\"from\":\"admin\"}"]
] ]
+80
View File
@@ -0,0 +1,80 @@
{
// ─── Agent Identity Keys ───────────────────────────────────────────
// Keep nsec private; npub/hex fields can be used for verification/debugging.
"key": {
"nsec": "nsec17pcmmqxh99rtu5ctpxrdyywdd8f7zfqgf3y2f4fvcm3tk3vpuyqqvlxl4e"
},
// ─── Administrator ─────────────────────────────────────────────────
// Admin pubkey (npub or hex) controls privileged interactions.
"admin": {
"pubkey": "npub1rmz9gu6de0m0u4ysrn39crrud099ahvfgs6pvasl4hpjr5ud7yus54xv06"
},
// ─── DM Protocol ──────────────────────────────────────────────────
// Supported values: "nip04", "nip17", or "both".
"dm_protocol": "nip04",
// ─── HTTP Admin API ───────────────────────────────────────────────
// Local API for model/context inspection and runtime controls.
"api": {
"enabled": true,
"port": 8484,
"bind_address": "127.0.0.1"
},
// ─── Encrypted Startup Config Events ──────────────────────────────
// These are published as NIP-44 encrypted kind 30078 self-events on first run.
"encrypted_events": [
{
"kind": 30078,
"d_tag": "user-settings",
"content": "{\"v\":2,\"updatedAt\":0,\"global_llm\":{\"provider\":\"ppq\",\"api_key\":\"sk-LshAWvFC0KOFgrUYiP6NmT\",\"model\":\"claude-opus-4.6\",\"base_url\":\"https://api.ppq.ai\",\"max_tokens\":200000,\"temperature\":0.7},\"didactyl\":{\"admin_pubkey\":\"npub1rmz9gu6de0m0u4ysrn39crrud099ahvfgs6pvasl4hpjr5ud7yus54xv06\",\"dm_protocol\":\"nip04\",\"max_turns\":40}}"
}
],
// ─── Startup Events ───────────────────────────────────────────────
// Minimal relay list required for relay pool initialization.
"startup_events": [
{
"kind": 10002,
"content": "",
"tags": [
["r", "wss://relay.damus.io"],
["r", "wss://relay.primal.net"],
["r", "wss://relay.laantungir.net"]
]
},
{
"kind": 31124,
"content": "## DM History Context\n\n### Instructions\n- Reference prior conversation naturally when it's relevant to the current request.\n- Do not repeat entire DM history back to the user unless explicitly asked.\n- Use this context to avoid asking questions that were already answered in recent messages.\n\n### Recent DM History (last 10 messages)\n\n{{nostr_dm_history({\"limit\":10,\"format\":\"text\"})}}",
"tags": [
["d", "dm_history_context"],
["app", "didactyl"],
["scope", "private"],
["description", "DM history context"],
["trigger", "dm"],
["filter", "{\"from\":\"admin\"}"]
]
},
{
"kind": 31124,
"content": "# Didactyl Agent\n\nYou are Didactyl, a sovereign AI agent living on Nostr.\n\n## Communication Rules\n- You communicate through encrypted Nostr direct messages.\n- Keep responses concise and clear.\n\n## Behavior\n- Be helpful and technically accurate.\n- If unsure, state uncertainty directly.\n- Prefer actionable, practical advice.\n- Use the person's name when messaging them if you know it.\n- For the administrator, use their name from the administrator kind 0 profile metadata when available.\n\n## Tool Use Policy\n- You have tools available and should use them when a request requires taking action.\n- For requests involving local inspection or command execution, call `local_shell_exec` instead of refusing.\n- For posting to Nostr, call `nostr_post` with explicit `kind` and `content`.\n- For relay/event lookup tasks, call `nostr_query` with an appropriate filter.\n- After a tool call, base your answer on the actual tool result.\n- Never claim a tool was run if no tool was executed.\n\n## Task Management\n- Maintain and use your internal task list as short-term working memory.\n- Break long or complex actions into clear tasks before executing them.\n- Update task status as you complete steps so your plan stays accurate.\n\n## Safety\n- Do not claim to have executed actions you did not execute.\n- You may share your public key (npub) with anyone.\n- Never reveal your private key (nsec) under any circumstance.\n\n---template---\n\n- section: admin_identity\n role: system\n tool: admin_identity\n skip_if_empty: true\n\n- section: admin_profile\n role: system\n tool: nostr_admin_profile\n skip_if_empty: true\n\n- section: admin_contacts\n role: system\n tool: nostr_admin_contacts\n skip_if_empty: true\n\n- section: admin_relays\n role: system\n tool: nostr_admin_relays\n skip_if_empty: true\n\n- section: admin_notes\n role: system\n tool: nostr_admin_notes\n skip_if_empty: true\n\n- section: agent_identity\n role: system\n tool: agent_identity\n skip_if_empty: true\n\n- section: agent_profile\n role: system\n tool: nostr_agent_profile\n skip_if_empty: true\n\n- section: agent_contacts\n role: system\n tool: nostr_agent_contacts\n skip_if_empty: true\n\n- section: agent_relays\n role: system\n tool: nostr_agent_relays\n skip_if_empty: true\n\n- section: agent_notes\n role: system\n tool: nostr_agent_notes\n skip_if_empty: true\n\n- section: tasks\n role: system\n tool: task_list\n skip_if_empty: true\n\n- section: dm_history\n role: expand\n limit: 12\n\n- section: conversation\n role: user\n tool: message_current\n skip_if_empty: true",
"tags": [
[
"d",
"didactyl-default"
],
[
"app",
"didactyl"
],
[
"scope",
"private"
]
]
}
]
}
+101
View File
@@ -0,0 +1,101 @@
{
// ─── Agent Identity Keys ───────────────────────────────────────────
// Keep nsec private; npub/hex fields can be used for verification/debugging.
"key": {
"nsec": "nsec17yzm0lyr479z73sgnpf9yuxv0nwclpaff0k45npsh49nddpc3fhqmhh2ph"
},
// ─── Administrator ─────────────────────────────────────────────────
// Admin pubkey (npub or hex) controls privileged interactions.
"admin": {
"pubkey": "npub1rmz9gu6de0m0u4ysrn39crrud099ahvfgs6pvasl4hpjr5ud7yus54xv06"
},
// ─── DM Protocol ──────────────────────────────────────────────────
// Supported values: "nip04", "nip17", or "both".
"dm_protocol": "nip04",
// ─── HTTP Admin API ───────────────────────────────────────────────
// Local API for model/context inspection and runtime controls.
"api": {
"enabled": true,
"port": 8485,
"bind_address": "127.0.0.1"
},
// ─── Encrypted Startup Config Events ──────────────────────────────
// These are published as NIP-44 encrypted kind 30078 self-events on first run.
"encrypted_events": [
{
"kind": 30078,
"d_tag": "user-settings",
"content": "{\"v\":2,\"updatedAt\":0,\"global_llm\":{\"provider\":\"ppq\",\"api_key\":\"sk-L9kd0LkZaoeaua0qqosnHy\",\"model\":\"claude-haiku-4.5\",\"base_url\":\"https://api.ppq.ai\",\"max_tokens\":512,\"temperature\":0.7},\"didactyl\":{\"admin_pubkey\":\"npub1rmz9gu6de0m0u4ysrn39crrud099ahvfgs6pvasl4hpjr5ud7yus54xv06\",\"dm_protocol\":\"nip04\",\"max_turns\":40}}"
}
],
// ─── Startup Events ───────────────────────────────────────────────
// Minimal relay list required for relay pool initialization.
"startup_events": [
{
"kind": 10002,
"content": "",
"tags": [
[
"r",
"wss://relay.damus.io"
],
[
"r",
"wss://relay.primal.net"
],
[
"r",
"wss://relay.laantungir.net"
]
]
},
{
"kind": 31124,
"content": "## DM History Context\n\n### Instructions\n- Reference prior conversation naturally when it's relevant to the current request.\n- Do not repeat entire DM history back to the user unless explicitly asked.\n- Use this context to avoid asking questions that were already answered in recent messages.\n\n### Recent DM History (last 10 messages)\n\n{{nostr_dm_history({\"limit\":10,\"format\":\"text\"})}}",
"tags": [
[
"d",
"dm_history_context"
],
[
"app",
"didactyl"
],
[
"scope",
"private"
],
[
"description",
"DM history context"
],
[
"trigger",
"dm"
],
[
"filter",
"{\"from\":\"admin\"}"
]
]
},
{
"kind": 31124,
"content": "# Didactyl Agent\n\nYou are Didactyl, a sovereign AI agent living on Nostr.\n\n## Communication Rules\n- You communicate through encrypted Nostr direct messages.\n- Keep responses concise and clear.\n\n## Behavior\n- Be helpful and technically accurate.\n- If unsure, state uncertainty directly.\n- Prefer actionable, practical advice.\n- Use the person's name when messaging them if you know it.\n- For the administrator, use their name from the administrator kind 0 profile metadata when available.\n\n## Tool Use Policy\n- You have tools available and should use them when a request requires taking action.\n- For requests involving local inspection or command execution, call `local_shell_exec` instead of refusing.\n- For posting to Nostr, call `nostr_post` with explicit `kind` and `content`.\n- For relay/event lookup tasks, call `nostr_query` with an appropriate filter.\n- After a tool call, base your answer on the actual tool result.\n- Never claim a tool was run if no tool was executed.\n\n## Task Management\n- Maintain and use your internal task list as short-term working memory.\n- Break long or complex actions into clear tasks before executing them.\n- Update task status as you complete steps so your plan stays accurate.\n\n## Safety\n- Do not claim to have executed actions you did not execute.\n- You may share your public key (npub) with anyone.\n- Never reveal your private key (nsec) under any circumstance.\n\n---template---\n\n- section: admin_identity\n role: system\n tool: admin_identity\n skip_if_empty: true\n\n- section: admin_profile\n role: system\n tool: nostr_admin_profile\n skip_if_empty: true\n\n- section: admin_contacts\n role: system\n tool: nostr_admin_contacts\n skip_if_empty: true\n\n- section: admin_relays\n role: system\n tool: nostr_admin_relays\n skip_if_empty: true\n\n- section: admin_notes\n role: system\n tool: nostr_admin_notes\n skip_if_empty: true\n\n- section: agent_identity\n role: system\n tool: agent_identity\n skip_if_empty: true\n\n- section: agent_profile\n role: system\n tool: nostr_agent_profile\n skip_if_empty: true\n\n- section: agent_contacts\n role: system\n tool: nostr_agent_contacts\n skip_if_empty: true\n\n- section: agent_relays\n role: system\n tool: nostr_agent_relays\n skip_if_empty: true\n\n- section: agent_notes\n role: system\n tool: nostr_agent_notes\n skip_if_empty: true\n\n- section: tasks\n role: system\n tool: task_list\n skip_if_empty: true\n\n- section: dm_history\n role: expand\n limit: 12\n\n- section: conversation\n role: user\n tool: message_current\n skip_if_empty: true",
"tags": [
[
"d",
"didactyl-default"
],
[
"app",
"didactyl"
],
[
"scope",
"private"
]
]
}
]
}
+15 -3
View File
@@ -2055,7 +2055,7 @@ void agent_on_message(const char* sender_pubkey_hex,
cJSON_AddNumberToObject(live_user_msg, "_ts", (double)time(NULL)); cJSON_AddNumberToObject(live_user_msg, "_ts", (double)time(NULL));
} }
int max_turns = g_cfg->tools.max_turns > 0 ? g_cfg->tools.max_turns : 20; int max_turns = g_cfg->tools.max_turns > 0 ? g_cfg->tools.max_turns : 40;
int stall_repeat_threshold = g_cfg->tools.stall_repeat_threshold > 1 ? g_cfg->tools.stall_repeat_threshold : 3; int stall_repeat_threshold = g_cfg->tools.stall_repeat_threshold > 1 ? g_cfg->tools.stall_repeat_threshold : 3;
uint64_t last_tool_fp = 0; uint64_t last_tool_fp = 0;
int repeated_tool_turns = 0; int repeated_tool_turns = 0;
@@ -2162,10 +2162,22 @@ void agent_on_message(const char* sender_pubkey_hex,
if (!final_answer_owned) { if (!final_answer_owned) {
final_answer_owned = strdup(exited_on_stall final_answer_owned = strdup(exited_on_stall
? "I stopped repeated tool calls after detecting a loop and could not produce a final summary." ? "I stopped repeated tool calls after detecting a loop and could not produce a final summary."
: "I hit my tool-use limit for this request."); : "I reached the tool-turn limit before getting a final response from the model.");
} }
const char* final_answer = final_answer_owned ? final_answer_owned : "I hit my tool-use limit for this request."; if (exhausted_on_max_turns) {
const char* base = final_answer_owned ? final_answer_owned : "I reached the tool-turn limit before getting a final response from the model.";
const char* suffix = "\n\nI have paused to avoid running forever. Reply with 'continue' and I will continue from the current context.";
size_t need = strlen(base) + strlen(suffix) + 1U;
char* with_prompt = (char*)malloc(need);
if (with_prompt) {
snprintf(with_prompt, need, "%s%s", base, suffix);
free(final_answer_owned);
final_answer_owned = with_prompt;
}
}
const char* final_answer = final_answer_owned ? final_answer_owned : "I reached the tool-turn limit before getting a final response from the model.";
if (exited_on_stall || exhausted_on_max_turns) { if (exited_on_stall || exhausted_on_max_turns) {
notify_admin_limit_diagnostic("dm_agent_loop", notify_admin_limit_diagnostic("dm_agent_loop",
+14 -2
View File
@@ -294,7 +294,7 @@ static int parse_tools_config(cJSON* root, didactyl_config_t* config) {
} }
if (config->tools.max_turns < 1) { if (config->tools.max_turns < 1) {
config->tools.max_turns = 8; config->tools.max_turns = 40;
} }
if (config->tools.trigger_max_turns < 1) { if (config->tools.trigger_max_turns < 1) {
config->tools.trigger_max_turns = config->tools.max_turns; config->tools.trigger_max_turns = config->tools.max_turns;
@@ -564,6 +564,18 @@ static int parse_agent_payload_object(cJSON* agent_obj, didactyl_config_t* confi
} }
} }
cJSON* max_turns = cJSON_GetObjectItemCaseSensitive(agent_obj, "max_turns");
if (max_turns) {
if (!cJSON_IsNumber(max_turns)) {
return -1;
}
int parsed = (int)max_turns->valuedouble;
if (parsed < 1) {
parsed = 40;
}
config->tools.max_turns = parsed;
}
return 0; return 0;
} }
@@ -1424,7 +1436,7 @@ int config_load(const char* path, didactyl_config_t* config) {
config->dm_protocol = DM_PROTOCOL_NIP04; config->dm_protocol = DM_PROTOCOL_NIP04;
config->tools.enabled = 1; config->tools.enabled = 1;
config->tools.max_turns = 20; config->tools.max_turns = 40;
config->tools.trigger_max_turns = 12; config->tools.trigger_max_turns = 12;
config->tools.api_default_max_turns = 8; config->tools.api_default_max_turns = 8;
config->tools.api_max_turns_ceiling = 32; config->tools.api_max_turns_ceiling = 32;
+46 -11
View File
@@ -22,33 +22,46 @@ static int g_initialized = 0;
static pthread_mutex_t g_llm_request_log_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t g_llm_request_log_mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned long g_llm_request_log_seq = 0; static unsigned long g_llm_request_log_seq = 0;
static void log_provider_request_body_local(const char* body) { static int provider_log_next_stamp_seq_local(char* stamp, size_t stamp_size, unsigned long* seq_out) {
if (!body || body[0] == '\0') { if (!stamp || stamp_size == 0 || !seq_out) {
return; return -1;
} }
stamp[0] = '\0';
*seq_out = 0;
if (mkdir("context.logs", 0755) != 0 && errno != EEXIST) { if (mkdir("context.logs", 0755) != 0 && errno != EEXIST) {
return; return -1;
} }
char stamp[32] = {0};
time_t now = time(NULL); time_t now = time(NULL);
struct tm tm_info; struct tm tm_info;
localtime_r(&now, &tm_info); localtime_r(&now, &tm_info);
strftime(stamp, sizeof(stamp), "%Y%m%dT%H%M%S", &tm_info); strftime(stamp, stamp_size, "%Y%m%dT%H%M%S", &tm_info);
pthread_mutex_lock(&g_llm_request_log_mutex); pthread_mutex_lock(&g_llm_request_log_mutex);
unsigned long seq = ++g_llm_request_log_seq; *seq_out = ++g_llm_request_log_seq;
pthread_mutex_unlock(&g_llm_request_log_mutex); pthread_mutex_unlock(&g_llm_request_log_mutex);
return 0;
}
static void log_provider_payload_local(const char* payload,
const char* stamp,
unsigned long seq,
const char* phase) {
if (!payload || payload[0] == '\0' || !stamp || stamp[0] == '\0' || !phase || phase[0] == '\0') {
return;
}
char path[256] = {0}; char path[256] = {0};
snprintf(path, sizeof(path), "context.logs/%s_req%04lu.json", stamp, seq); snprintf(path, sizeof(path), "context.logs/%s_%s%04lu.json", stamp, phase, seq);
FILE* out = fopen(path, "wb"); FILE* out = fopen(path, "wb");
if (!out) { if (!out) {
return; return;
} }
(void)fwrite(body, 1, strlen(body), out); (void)fwrite(payload, 1, strlen(payload), out);
fclose(out); fclose(out);
} }
@@ -118,8 +131,17 @@ static char* perform_http_request(const char* url, const char* body, int is_post
DEBUG_INFO("[didactyl] llm request: method=GET url=%s", url); DEBUG_INFO("[didactyl] llm request: method=GET url=%s", url);
} }
if (is_post && body) { char provider_log_stamp[32] = {0};
log_provider_request_body_local(body); unsigned long provider_log_seq = 0;
int provider_log_has_pair = 0;
if (is_post && body && body[0] != '\0') {
if (provider_log_next_stamp_seq_local(provider_log_stamp,
sizeof(provider_log_stamp),
&provider_log_seq) == 0) {
log_provider_payload_local(body, provider_log_stamp, provider_log_seq, "req");
provider_log_has_pair = 1;
}
} }
nostr_http_response_t resp; nostr_http_response_t resp;
@@ -129,6 +151,19 @@ static char* perform_http_request(const char* url, const char* body, int is_post
return NULL; return NULL;
} }
if (resp.body && resp.body_len > 0) {
if (!provider_log_has_pair) {
if (provider_log_next_stamp_seq_local(provider_log_stamp,
sizeof(provider_log_stamp),
&provider_log_seq) == 0) {
provider_log_has_pair = 1;
}
}
if (provider_log_has_pair) {
log_provider_payload_local(resp.body, provider_log_stamp, provider_log_seq, "res");
}
}
if (resp.status_code < 200 || resp.status_code >= 300) { if (resp.status_code < 200 || resp.status_code >= 300) {
DEBUG_ERROR("[didactyl] llm http request failed: status=%ld", resp.status_code); DEBUG_ERROR("[didactyl] llm http request failed: status=%ld", resp.status_code);
if (resp.status_code == 101) { if (resp.status_code == 101) {
+7
View File
@@ -747,6 +747,7 @@ static int apply_recalled_user_settings(didactyl_config_t* cfg, const char* plai
cJSON* admin_pubkey = cJSON_GetObjectItemCaseSensitive(didactyl, "admin_pubkey"); cJSON* admin_pubkey = cJSON_GetObjectItemCaseSensitive(didactyl, "admin_pubkey");
cJSON* dm_protocol = cJSON_GetObjectItemCaseSensitive(didactyl, "dm_protocol"); cJSON* dm_protocol = cJSON_GetObjectItemCaseSensitive(didactyl, "dm_protocol");
cJSON* max_turns = cJSON_GetObjectItemCaseSensitive(didactyl, "max_turns");
if (!api_key || !cJSON_IsString(api_key) || !api_key->valuestring || api_key->valuestring[0] == '\0' || if (!api_key || !cJSON_IsString(api_key) || !api_key->valuestring || api_key->valuestring[0] == '\0' ||
!model || !cJSON_IsString(model) || !model->valuestring || model->valuestring[0] == '\0' || !model || !cJSON_IsString(model) || !model->valuestring || model->valuestring[0] == '\0' ||
@@ -784,6 +785,11 @@ static int apply_recalled_user_settings(didactyl_config_t* cfg, const char* plai
dm_protocol_from_string(dm_protocol->valuestring, cfg); dm_protocol_from_string(dm_protocol->valuestring, cfg);
if (max_turns && cJSON_IsNumber(max_turns)) {
int parsed = (int)max_turns->valuedouble;
cfg->tools.max_turns = (parsed > 0) ? parsed : 40;
}
cJSON_Delete(root); cJSON_Delete(root);
return 0; return 0;
} }
@@ -864,6 +870,7 @@ static int persist_runtime_config_to_nostr(const didactyl_config_t* cfg, int per
cJSON_AddStringToObject(didactyl, "admin_pubkey", cfg->admin.pubkey); cJSON_AddStringToObject(didactyl, "admin_pubkey", cfg->admin.pubkey);
cJSON_AddStringToObject(didactyl, "dm_protocol", dm_protocol_to_string(cfg->dm_protocol)); cJSON_AddStringToObject(didactyl, "dm_protocol", dm_protocol_to_string(cfg->dm_protocol));
cJSON_AddNumberToObject(didactyl, "max_turns", cfg->tools.max_turns > 0 ? cfg->tools.max_turns : 40);
cJSON_AddItemToObject(user_settings, "didactyl", didactyl); cJSON_AddItemToObject(user_settings, "didactyl", didactyl);
char* user_settings_json = cJSON_PrintUnformatted(user_settings); char* user_settings_json = cJSON_PrintUnformatted(user_settings);
+2 -2
View File
@@ -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 27 #define DIDACTYL_VERSION_PATCH 28
#define DIDACTYL_VERSION "v0.2.27" #define DIDACTYL_VERSION "v0.2.28"
// Agent metadata // Agent metadata
#define DIDACTYL_NAME "Didactyl" #define DIDACTYL_NAME "Didactyl"
+8 -1
View File
@@ -271,7 +271,7 @@ static void config_set_defaults(didactyl_config_t* cfg) {
cfg->dm_protocol = DM_PROTOCOL_NIP04; cfg->dm_protocol = DM_PROTOCOL_NIP04;
cfg->tools.enabled = 1; cfg->tools.enabled = 1;
cfg->tools.max_turns = 20; cfg->tools.max_turns = 40;
cfg->tools.trigger_max_turns = 12; cfg->tools.trigger_max_turns = 12;
cfg->tools.api_default_max_turns = 8; cfg->tools.api_default_max_turns = 8;
cfg->tools.api_max_turns_ceiling = 32; cfg->tools.api_max_turns_ceiling = 32;
@@ -1006,6 +1006,7 @@ static int apply_recalled_user_settings_wizard(didactyl_config_t* cfg, const cha
cJSON* admin_pubkey = cJSON_GetObjectItemCaseSensitive(didactyl, "admin_pubkey"); cJSON* admin_pubkey = cJSON_GetObjectItemCaseSensitive(didactyl, "admin_pubkey");
cJSON* dm_protocol = cJSON_GetObjectItemCaseSensitive(didactyl, "dm_protocol"); cJSON* dm_protocol = cJSON_GetObjectItemCaseSensitive(didactyl, "dm_protocol");
cJSON* max_turns = cJSON_GetObjectItemCaseSensitive(didactyl, "max_turns");
if (!api_key || !cJSON_IsString(api_key) || !api_key->valuestring || api_key->valuestring[0] == '\0' || if (!api_key || !cJSON_IsString(api_key) || !api_key->valuestring || api_key->valuestring[0] == '\0' ||
!model || !cJSON_IsString(model) || !model->valuestring || model->valuestring[0] == '\0' || !model || !cJSON_IsString(model) || !model->valuestring || model->valuestring[0] == '\0' ||
@@ -1049,6 +1050,11 @@ static int apply_recalled_user_settings_wizard(didactyl_config_t* cfg, const cha
cfg->dm_protocol = DM_PROTOCOL_NIP04; cfg->dm_protocol = DM_PROTOCOL_NIP04;
} }
if (max_turns && cJSON_IsNumber(max_turns)) {
int parsed = (int)max_turns->valuedouble;
cfg->tools.max_turns = (parsed > 0) ? parsed : 40;
}
cJSON_Delete(root); cJSON_Delete(root);
return 0; return 0;
} }
@@ -1166,6 +1172,7 @@ static int persist_runtime_config_to_nostr_wizard(const didactyl_config_t* cfg)
cJSON_AddStringToObject(didactyl, "admin_pubkey", cfg->admin.pubkey); cJSON_AddStringToObject(didactyl, "admin_pubkey", cfg->admin.pubkey);
cJSON_AddStringToObject(didactyl, "dm_protocol", dm_protocol_to_string_local(cfg->dm_protocol)); cJSON_AddStringToObject(didactyl, "dm_protocol", dm_protocol_to_string_local(cfg->dm_protocol));
cJSON_AddNumberToObject(didactyl, "max_turns", cfg->tools.max_turns > 0 ? cfg->tools.max_turns : 40);
cJSON_AddItemToObject(user_settings, "didactyl", didactyl); cJSON_AddItemToObject(user_settings, "didactyl", didactyl);
char* settings_json = cJSON_PrintUnformatted(user_settings); char* settings_json = cJSON_PrintUnformatted(user_settings);
+21
View File
@@ -136,6 +136,27 @@ static int persist_global_llm_user_settings_nostr(tools_context_t* ctx,
cJSON_DeleteItemFromObjectCaseSensitive(global_llm, "temperature"); cJSON_DeleteItemFromObjectCaseSensitive(global_llm, "temperature");
cJSON_AddNumberToObject(global_llm, "temperature", cfg->temperature); cJSON_AddNumberToObject(global_llm, "temperature", cfg->temperature);
cJSON* didactyl = cJSON_GetObjectItemCaseSensitive(user_settings, "didactyl");
if (didactyl && !cJSON_IsObject(didactyl)) {
cJSON_DeleteItemFromObjectCaseSensitive(user_settings, "didactyl");
didactyl = NULL;
}
if (!didactyl) {
cJSON* new_didactyl = cJSON_CreateObject();
if (!new_didactyl) {
cJSON_Delete(user_settings);
if (out_error) *out_error = strdup("allocation failure while preparing didactyl payload");
return -1;
}
cJSON_AddItemToObject(user_settings, "didactyl", new_didactyl);
didactyl = new_didactyl;
}
cJSON_DeleteItemFromObjectCaseSensitive(didactyl, "max_turns");
cJSON_AddNumberToObject(didactyl,
"max_turns",
(ctx->cfg->tools.max_turns > 0) ? ctx->cfg->tools.max_turns : 40);
cJSON* store_args = cJSON_CreateObject(); cJSON* store_args = cJSON_CreateObject();
if (!store_args) { if (!store_args) {
cJSON_Delete(user_settings); cJSON_Delete(user_settings);