v0.0.6 - Remove top-level agent config, move SYSTEM prompt into Soul startup event, and update Quick Start binary flow

This commit is contained in:
Your Name
2026-02-28 08:27:29 -04:00
parent 76842627dc
commit 1c69a581d9
7 changed files with 20 additions and 136 deletions
+17 -13
View File
@@ -26,13 +26,25 @@ Because all identity, communication, and memory live on Nostr, the agent is **po
## Quick Start
### Prerequisites
### Download binary (recommended)
1. Download the latest release binary from GitLab: <https://git.laantungir.net/laantungir/didactyl/-/releases>
2. Make it executable and run it:
```bash
chmod +x ./didactyl_static_x86_64
./didactyl_static_x86_64 --config ./config.json
```
### Build from source (optional)
#### Prerequisites
- Docker (for static binary build)
- An OpenAI-compatible LLM API key (OpenAI, PPQ, Ollama, etc.)
- A Nostr keypair (nsec)
### Build
#### Build
```bash
./build_static.sh # builds a fully static MUSL binary via Docker
@@ -44,14 +56,6 @@ Edit [`config.json`](config.json):
```json
{
"agent": {
"name": "Didactyl Agent",
"display_name": "Didactyl",
"about": "A sovereign AI agent on Nostr",
"picture": "https://...",
"banner": "https://...",
"nip05": ""
},
"keys": {
"nsec": "nsec1...",
"npub": "npub1...",
@@ -108,13 +112,13 @@ Edit [`config.json`](config.json):
### Run
```bash
./didactyl
./didactyl_static_x86_64 --config ./config.json
```
Options:
```
./didactyl --config <path> # custom config file (default: ./config.json)
./didactyl --debug <0-5> # log verbosity (0 none, 3 info, 5 trace)
./didactyl_static_x86_64 --config <path> # custom config file (default: ./config.json)
./didactyl_static_x86_64 --debug <0-5> # log verbosity (0 none, 3 info, 5 trace)
```
### Talk to it
-24
View File
@@ -1,24 +0,0 @@
# Didactyl Agent
You are Didactyl, a sovereign AI agent living on Nostr.
## Communication Rules
- You communicate through encrypted Nostr direct messages.
- Keep responses concise and clear.
## Behavior
- Be helpful and technically accurate.
- If unsure, state uncertainty directly.
- Prefer actionable, practical advice.
## Tool Use Policy
- You have tools available and should use them when a request requires taking action.
- For requests involving local inspection or command execution, call `shell_exec` instead of refusing.
- For posting to Nostr, call `nostr_post` with explicit `kind` and `content`.
- For relay/event lookup tasks, call `nostr_query` with an appropriate filter.
- After a tool call, base your answer on the actual tool result.
- Never claim a tool was run if no tool was executed.
## Safety
- Do not claim to have executed actions you did not execute.
- Do not reveal secrets from configuration or keys.
+1 -19
View File
@@ -417,34 +417,16 @@ int config_load(const char* path, didactyl_config_t* config) {
int rc = -1;
cJSON* agent = cJSON_GetObjectItemCaseSensitive(root, "agent");
cJSON* keys = cJSON_GetObjectItemCaseSensitive(root, "keys");
cJSON* admin = cJSON_GetObjectItemCaseSensitive(root, "admin");
cJSON* llm = cJSON_GetObjectItemCaseSensitive(root, "llm");
if (!agent || !cJSON_IsObject(agent) ||
!keys || !cJSON_IsObject(keys) ||
if (!keys || !cJSON_IsObject(keys) ||
!admin || !cJSON_IsObject(admin) ||
!llm || !cJSON_IsObject(llm)) {
goto cleanup;
}
if (copy_json_string(agent, "name", config->profile.name, sizeof(config->profile.name), 1) != 0) {
goto cleanup;
}
if (copy_json_string(agent, "display_name", config->profile.display_name, sizeof(config->profile.display_name), 1) != 0) {
goto cleanup;
}
if (copy_json_string(agent, "about", config->profile.about, sizeof(config->profile.about), 1) != 0) {
goto cleanup;
}
if (copy_json_string(agent, "picture", config->profile.picture, sizeof(config->profile.picture), 0) != 0) {
goto cleanup;
}
if (copy_json_string(agent, "nip05", config->profile.nip05, sizeof(config->profile.nip05), 0) != 0) {
goto cleanup;
}
if (copy_json_string(keys, "nsec", config->keys.nsec, sizeof(config->keys.nsec), 1) != 0) {
goto cleanup;
}
-9
View File
@@ -9,14 +9,6 @@
#define OW_MAX_KEY_LEN 256
#define OW_MAX_MODEL_LEN 128
typedef struct {
char name[OW_MAX_NAME_LEN];
char display_name[OW_MAX_NAME_LEN];
char about[OW_MAX_ABOUT_LEN];
char picture[OW_MAX_URL_LEN];
char nip05[OW_MAX_URL_LEN];
} agent_profile_t;
typedef struct {
char nsec[OW_MAX_KEY_LEN];
unsigned char private_key[32];
@@ -57,7 +49,6 @@ typedef struct {
} startup_event_t;
typedef struct {
agent_profile_t profile;
agent_keys_t keys;
admin_config_t admin;
char** relays;
+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 0
#define DIDACTYL_VERSION_PATCH 5
#define DIDACTYL_VERSION "v0.0.5"
#define DIDACTYL_VERSION_PATCH 6
#define DIDACTYL_VERSION "v0.0.6"
// Agent metadata
#define DIDACTYL_NAME "Didactyl"
-68
View File
@@ -390,74 +390,6 @@ int nostr_handler_init(didactyl_config_t* config) {
return 0;
}
int nostr_handler_publish_profile(void) {
if (!g_cfg || !g_pool) {
return -1;
}
DEBUG_INFO("[didactyl] publish_profile: build metadata payload");
cJSON* profile = cJSON_CreateObject();
if (!profile) {
return -1;
}
cJSON_AddStringToObject(profile, "name", g_cfg->profile.name);
cJSON_AddStringToObject(profile, "display_name", g_cfg->profile.display_name);
cJSON_AddStringToObject(profile, "about", g_cfg->profile.about);
cJSON_AddStringToObject(profile, "picture", g_cfg->profile.picture);
if (g_cfg->profile.nip05[0] != '\0') {
cJSON_AddStringToObject(profile, "nip05", g_cfg->profile.nip05);
}
char* content = cJSON_PrintUnformatted(profile);
cJSON_Delete(profile);
if (!content) {
return -1;
}
DEBUG_INFO("[didactyl] publish_profile: sign kind-0 event");
cJSON* event = nostr_create_and_sign_event(0, content, NULL, g_cfg->keys.private_key, time(NULL));
free(content);
if (!event) {
return -1;
}
const char** connected_relays = (const char**)calloc((size_t)g_cfg->relay_count, sizeof(char*));
if (!connected_relays) {
cJSON_Delete(event);
return -1;
}
int connected_count = 0;
for (int i = 0; i < g_cfg->relay_count; i++) {
if (nostr_relay_pool_get_relay_status(g_pool, g_cfg->relays[i]) == NOSTR_POOL_RELAY_CONNECTED) {
connected_relays[connected_count++] = g_cfg->relays[i];
}
}
if (connected_count == 0) {
DEBUG_WARN("[didactyl] publish_profile: no connected relays yet, deferring publish");
free(connected_relays);
cJSON_Delete(event);
return 0;
}
DEBUG_INFO("[didactyl] publish_profile: publishing to %d connected relay(s)", connected_count);
int sent = nostr_relay_pool_publish_async(
g_pool,
connected_relays,
connected_count,
event,
NULL,
NULL);
free(connected_relays);
cJSON_Delete(event);
DEBUG_INFO("[didactyl] publish profile result: sent_to=%d connected relay(s)", sent);
return sent > 0 ? 0 : -1;
}
int nostr_handler_subscribe_dms(dm_callback_t callback, void* user_data) {
if (!g_cfg || !g_pool || !callback) {
return -1;
-1
View File
@@ -7,7 +7,6 @@
typedef void (*dm_callback_t)(const char* sender_pubkey_hex, const char* message, void* user_data);
int nostr_handler_init(didactyl_config_t* config);
int nostr_handler_publish_profile(void);
int nostr_handler_subscribe_dms(dm_callback_t callback, void* user_data);
int nostr_handler_send_dm(const char* recipient_pubkey_hex, const char* message);
int nostr_handler_publish_kind_event(int kind, const char* content, cJSON* tags);