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 6edad064c6
commit b9526c3a20
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.
## Current Status — v0.2.27
## Current Status — v0.2.28
**Active build — this project is barely working. Experiment at your own risk.**
> Last release update: v0.2.27Remove markdown context snapshot logging; keep only raw provider request JSON logging and simplify context debug plumbing
> Last release update: v0.2.28Updated 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
- Publishes configured startup events per relay as each relay becomes connected
+4 -4
View File
@@ -24,7 +24,7 @@
{
"kind": 30078,
"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,
"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": [
["d", "dm_history"],
["d", "dm_history_context"],
["app", "didactyl"],
["scope", "private"],
["description", "DM conversation history for context continuity"],
["description", "DM history context"],
["trigger", "dm"],
["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));
}
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;
uint64_t last_tool_fp = 0;
int repeated_tool_turns = 0;
@@ -2162,10 +2162,22 @@ void agent_on_message(const char* sender_pubkey_hex,
if (!final_answer_owned) {
final_answer_owned = strdup(exited_on_stall
? "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) {
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) {
config->tools.max_turns = 8;
config->tools.max_turns = 40;
}
if (config->tools.trigger_max_turns < 1) {
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;
}
@@ -1424,7 +1436,7 @@ int config_load(const char* path, didactyl_config_t* config) {
config->dm_protocol = DM_PROTOCOL_NIP04;
config->tools.enabled = 1;
config->tools.max_turns = 20;
config->tools.max_turns = 40;
config->tools.trigger_max_turns = 12;
config->tools.api_default_max_turns = 8;
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 unsigned long g_llm_request_log_seq = 0;
static void log_provider_request_body_local(const char* body) {
if (!body || body[0] == '\0') {
return;
static int provider_log_next_stamp_seq_local(char* stamp, size_t stamp_size, unsigned long* seq_out) {
if (!stamp || stamp_size == 0 || !seq_out) {
return -1;
}
stamp[0] = '\0';
*seq_out = 0;
if (mkdir("context.logs", 0755) != 0 && errno != EEXIST) {
return;
return -1;
}
char stamp[32] = {0};
time_t now = time(NULL);
struct tm 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);
unsigned long seq = ++g_llm_request_log_seq;
*seq_out = ++g_llm_request_log_seq;
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};
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");
if (!out) {
return;
}
(void)fwrite(body, 1, strlen(body), out);
(void)fwrite(payload, 1, strlen(payload), 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);
}
if (is_post && body) {
log_provider_request_body_local(body);
char provider_log_stamp[32] = {0};
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;
@@ -129,6 +151,19 @@ static char* perform_http_request(const char* url, const char* body, int is_post
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) {
DEBUG_ERROR("[didactyl] llm http request failed: status=%ld", resp.status_code);
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* 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' ||
!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);
if (max_turns && cJSON_IsNumber(max_turns)) {
int parsed = (int)max_turns->valuedouble;
cfg->tools.max_turns = (parsed > 0) ? parsed : 40;
}
cJSON_Delete(root);
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, "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);
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
#define DIDACTYL_VERSION_MAJOR 0
#define DIDACTYL_VERSION_MINOR 2
#define DIDACTYL_VERSION_PATCH 27
#define DIDACTYL_VERSION "v0.2.27"
#define DIDACTYL_VERSION_PATCH 28
#define DIDACTYL_VERSION "v0.2.28"
// Agent metadata
#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->tools.enabled = 1;
cfg->tools.max_turns = 20;
cfg->tools.max_turns = 40;
cfg->tools.trigger_max_turns = 12;
cfg->tools.api_default_max_turns = 8;
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* 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' ||
!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;
}
if (max_turns && cJSON_IsNumber(max_turns)) {
int parsed = (int)max_turns->valuedouble;
cfg->tools.max_turns = (parsed > 0) ? parsed : 40;
}
cJSON_Delete(root);
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, "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);
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_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();
if (!store_args) {
cJSON_Delete(user_settings);