diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f0c73a3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +.gitignore +.roo/ +*.log +*.nsec.debug.log +context.log.md +d-log.txt +tests/results/ + +# Keep nostr_core_lib sources but exclude VCS metadata to avoid huge build context +nostr_core_lib/.git/ +nostr_core_lib/**/.git/ diff --git a/.gitignore b/.gitignore index 5405d8f..6c6c06d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,7 @@ a.out # Local secrets genesis.jsonc +genesis.anvil.jsonc +anvil.service tests/configs/test_genesis.jsonc tests/configs/test_keys.jsonc diff --git a/Dockerfile.alpine-musl b/Dockerfile.alpine-musl index ad5b7b4..944180a 100644 --- a/Dockerfile.alpine-musl +++ b/Dockerfile.alpine-musl @@ -123,6 +123,7 @@ RUN NOSTR_LIB=$(ls /build/nostr_core_lib/libnostr_core_*.a 2>/dev/null | head -1 src/tools/tool_task.c src/tools/tool_nostr_list.c src/tools/tool_nostr_block.c src/tools/tool_local.c \ src/tools/tool_skill.c src/tools/tool_nostr_post.c src/tools/tool_memory.c src/tools/tool_config.c src/tools/tool_cashu_wallet.c src/tools/tool_blossom.c src/trigger_manager.c \ src/cashu_wallet.c src/nostr_block_list.c src/prompt_template.c src/http_api.c src/setup_wizard.c src/mongoose.c src/debug.c \ + src/json_to_markdown.c src/context_roles.c src/context_format.c \ -o /build/didactyl_static \ $NOSTR_LIB \ -lsecp256k1 \ diff --git a/Makefile b/Makefile index 742d603..763b23a 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,10 @@ SRCS = \ $(SRC_DIR)/setup_wizard.c \ $(SRC_DIR)/mongoose.c \ $(SRC_DIR)/debug.c \ - $(SRC_DIR)/nostr_block_list.c + $(SRC_DIR)/nostr_block_list.c \ + $(SRC_DIR)/json_to_markdown.c \ + $(SRC_DIR)/context_roles.c \ + $(SRC_DIR)/context_format.c INCLUDES = \ -I$(SRC_DIR) \ @@ -113,4 +116,8 @@ clean: rm -f $(TARGET) rm -rf $(BUILD_DIR) -.PHONY: all deps clean force-version +test_pool: $(NOSTR_LIB) test_pool.c + @if [ -z "$(NOSTR_LIB)" ]; then echo "nostr_core_lib static library not found; run 'make deps'"; exit 1; fi + $(CC) $(CFLAGS) $(INCLUDES) -o $@ test_pool.c $(NOSTR_LIB) $(LDFLAGS) + +.PHONY: all deps clean force-version test_pool diff --git a/README.md b/README.md index 539cafe..8d8320b 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.20 +## Current Status — v0.2.21 **Active build — this project is barely working. Experiment at your own risk.** -> Last release update: v0.2.20 — Improve automated test harness process/log watching/reporting and refresh test config examples +> Last release update: v0.2.21 — Fix markdown context assembly and logging: add Anvil DM trigger skill config, preserve H1 title on fallback, convert full-mode turn snapshots to readable markdown, and sync Anvil service restart/deploy behavior - 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/build_static.sh b/build_static.sh index ba49823..2427c80 100755 --- a/build_static.sh +++ b/build_static.sh @@ -68,33 +68,52 @@ if [ -d "$SCRIPT_DIR/build" ]; then echo "Removed legacy build directory: $SCRIPT_DIR/build" fi -# Check if Docker is available -if ! command -v docker &> /dev/null; then - echo "ERROR: Docker is not installed or not in PATH" +# Detect available OCI container runtime (prefer Docker, fallback to Podman) +if command -v docker &> /dev/null; then + DOCKER_CMD="docker" + RUNTIME_NAME="Docker" +elif command -v podman &> /dev/null; then + DOCKER_CMD="podman" + RUNTIME_NAME="Podman" +else + echo "ERROR: No supported container runtime found in PATH" echo "" - echo "Docker is required to build MUSL static binaries." - echo "Please install Docker:" - echo " - Ubuntu/Debian: sudo apt install docker.io" - echo " - Or visit: https://docs.docker.com/engine/install/" + echo "This build requires either Docker or Podman." + echo "Install one of the following:" + echo " - Docker (Ubuntu/Debian): sudo apt install docker.io" + echo " - Podman (Ubuntu/Debian): sudo apt install podman" echo "" exit 1 fi -# Check if Docker daemon is running -if ! docker info &> /dev/null; then - echo "ERROR: Docker daemon is not running or user not in docker group" - echo "" - echo "Please start Docker and ensure you're in the docker group:" - echo " - sudo systemctl start docker" - echo " - sudo usermod -aG docker $USER && newgrp docker" - echo " - Or start Docker Desktop" - echo "" - exit 1 +# Check if runtime daemon/service is usable +if ! $DOCKER_CMD info &> /dev/null; then + # Docker is installed but current user may not yet have docker-group membership in this shell. + # If passwordless sudo is available, transparently fall back to sudo docker. + if [ "$DOCKER_CMD" = "docker" ] && command -v sudo &> /dev/null && sudo -n docker info &> /dev/null; then + DOCKER_CMD="sudo docker" + echo "⚠ Docker requires elevated privileges in this shell; using sudo docker" + echo "" + else + echo "ERROR: $RUNTIME_NAME is installed but not usable" + echo "" + if [ "$DOCKER_CMD" = "docker" ]; then + echo "Please start Docker and ensure your user can access it:" + echo " - sudo systemctl start docker" + echo " - sudo usermod -aG docker $USER && newgrp docker" + echo " - Or log out and back in after adding to docker group" + echo " - Or run this script with sudo" + else + echo "Please verify Podman is configured for your user:" + echo " - podman info" + echo " - For rootless setup, ensure subuid/subgid are configured" + fi + echo "" + exit 1 + fi fi -DOCKER_CMD="docker" - -echo "✓ Docker is available and running" +echo "✓ $RUNTIME_NAME is available and running" echo "" # Detect architecture (or use explicit target platform override) @@ -176,10 +195,10 @@ fi # Check if Alpine base image is cached echo "Checking for cached Alpine Docker image..." -if ! docker images alpine:3.19 --format "{{.Repository}}:{{.Tag}}" | grep -q "alpine:3.19"; then +if ! $DOCKER_CMD images alpine:3.19 --format "{{.Repository}}:{{.Tag}}" | grep -q "alpine:3.19"; then echo "⚠ Alpine 3.19 image not found in cache" echo "Attempting to pull Alpine 3.19 image..." - if ! docker pull alpine:3.19; then + if ! $DOCKER_CMD pull alpine:3.19; then echo "" echo "ERROR: Failed to pull Alpine 3.19 image" echo "This is required for the static build." @@ -239,11 +258,25 @@ $DOCKER_CMD cp "$CONTAINER_ID:/didactyl_static" "$OUTPUT_DIR/$OUTPUT_NAME" || { # Clean up container $DOCKER_CMD rm "$CONTAINER_ID" > /dev/null +# If runtime command used sudo, copied file may be root-owned; fix ownership for current user +if [ ! -w "$OUTPUT_DIR/$OUTPUT_NAME" ]; then + if command -v sudo &> /dev/null && sudo -n true &> /dev/null; then + sudo chown "$(id -u):$(id -g)" "$OUTPUT_DIR/$OUTPUT_NAME" || true + fi +fi + echo "✓ Binary extracted to: $OUTPUT_DIR/$OUTPUT_NAME" echo "" -# Make binary executable -chmod +x "$OUTPUT_DIR/$OUTPUT_NAME" +# Make binary executable (fallback to sudo if ownership/permissions still block) +chmod +x "$OUTPUT_DIR/$OUTPUT_NAME" 2>/dev/null || { + if command -v sudo &> /dev/null && sudo -n true &> /dev/null; then + sudo chmod +x "$OUTPUT_DIR/$OUTPUT_NAME" + else + echo "ERROR: Could not set executable bit on $OUTPUT_DIR/$OUTPUT_NAME" + exit 1 + fi +} # Verify the binary echo "==========================================" diff --git a/didactyl.code-workspace b/didactyl.code-workspace new file mode 100644 index 0000000..72154e4 --- /dev/null +++ b/didactyl.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../../anvil" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/docs/CONTEXT.md b/docs/CONTEXT.md index 95d7bb1..b88bf08 100644 --- a/docs/CONTEXT.md +++ b/docs/CONTEXT.md @@ -79,7 +79,7 @@ Trigger event occurs (DM, cron, subscription, webhook, chain) │ ├─ Skill has trigger matching this event? │ │ ├─ YES → add to context (layer 1) │ │ │ └─ Resolve {{...}} references (layer 2) - │ │ │ ├─ Known tool? → execute tool, insert result + │ │ │ ├─ Known tool? → execute tool, convert JSON to markdown, insert │ │ │ ├─ Adopted skill d-tag? → insert skill content │ │ │ └─ Unknown? → resolve to empty │ │ │ @@ -87,8 +87,13 @@ Trigger event occurs (DM, cron, subscription, webhook, chain) │ │ │ └─ Continue to next skill in list │ - ├─ Append triggering event payload - │ └─ For DM triggers: always append raw message content + ├─ Format Document + │ ├─ Add `# Agent Name` title + │ ├─ Bump all skill headings down one level (# -> ##) + │ └─ Concatenate skills with `---` separators + │ + ├─ Split Roles + │ └─ Parse `system:` and `user:` markers to build API messages array │ ├─ Attach tool schemas (filtered by skill requires_tool tags) │ @@ -100,35 +105,27 @@ Trigger event occurs (DM, cron, subscription, webhook, chain) ### Visualization -``` -╔══════════════════════════════════════════╗ -║ CONTEXT WINDOW ║ -║ ║ -║ ┌────────────────────────────────────┐ ║ -║ │ Layer 1: personality (dm trigger) │ ║ -║ │ │ ║ -║ │ ┌──────────────────────────────┐ │ ║ -║ │ │ Layer 2: {{identity}} │ │ ║ -║ │ │ You are Didactyl. npub1... │ │ ║ -║ │ └──────────────────────────────┘ │ ║ -║ │ │ ║ -║ │ You speak concisely and directly. │ ║ -║ └────────────────────────────────────┘ ║ -║ ┌────────────────────────────────────┐ ║ -║ │ Layer 1: chat (dm trigger) │ ║ -║ │ │ ║ -║ │ Respond helpfully to the admin. │ ║ -║ │ Use tools as needed. │ ║ -║ │ │ ║ -║ │ tools: [nostr_query, nostr_dm] │ ║ -║ └────────────────────────────────────┘ ║ -║ ┌────────────────────────────────────┐ ║ -║ │ DM content (always last) │ ║ -║ │ │ ║ -║ │ "Who mentioned me today?" │ ║ -║ └────────────────────────────────────┘ ║ -║ ║ -╚══════════════════════════════════════════╝ +The assembled context is a single, coherent markdown document before being split into API messages: + +```markdown +system: +# Didactyl Agent + +- **npub**: `npub1...` + +## Personality + +You speak concisely and directly. + +--- + +## Chat + +Respond helpfully to the admin. +Use tools as needed. + +user: +Who mentioned me today? ``` ### Why Order Matters @@ -157,11 +154,11 @@ Trigger event occurs (DM, cron, subscription, webhook, chain) When the engine encounters `{{variable_name}}` in a skill template: -1. **Check known tools** — if it matches a tool name, execute the tool and insert the result +1. **Check known tools** — if it matches a tool name, execute the tool. The tool returns JSON, which the template engine automatically converts into readable markdown (bullet lists, bold keys) before inserting it into the document. 2. **Check adopted skills** — if it matches an adopted skill's d-tag, insert that skill's content (layer 2) 3. **Neither** — resolve to empty (for portability) -This means `{{admin_profile}}` calls the `nostr_admin_profile` tool, while `{{identity}}` inserts the adopted "identity" skill's content. The skill author doesn't need to know which is which — the resolution is transparent. +This means `{{admin_profile}}` calls the `nostr_admin_profile` tool and formats its JSON output as markdown, while `{{identity}}` inserts the adopted "identity" skill's content. The skill author doesn't need to know which is which — the resolution is transparent. See [SKILLS.md — Template Variables](SKILLS.md#template-variables) for the full variable table. diff --git a/docs/SKILLS.md b/docs/SKILLS.md index 47c3b18..136f144 100644 --- a/docs/SKILLS.md +++ b/docs/SKILLS.md @@ -68,7 +68,7 @@ The `content` field of a skill event IS the template — markdown instructions t ```json { "kind": 31123, - "content": "system:\n# Spelling and Grammar Checker\n\nYou are a spelling and grammar checker.\n\n## Rules\n\n- Fix spelling errors\n- Fix grammar errors\n- Preserve original formatting\n- Return **ONLY** the corrected text, no explanations\n\nuser:\n{{message}}", + "content": "system:\n## Spelling and Grammar Checker\n\nYou are a spelling and grammar checker.\n\n### Rules\n\n- Fix spelling errors\n- Fix grammar errors\n- Preserve original formatting\n- Return **ONLY** the corrected text, no explanations\n\nuser:\n{{message}}", "tags": [ ["d", "spellcheck"], ["description", "Check spelling and grammar"], @@ -81,6 +81,7 @@ The `content` field of a skill event IS the template — markdown instructions t ``` - **`content`** — the template in markdown. May include `{{...}}` template variables and `system:` / `user:` role markers. This is what goes into the context window. +- **Heading Levels** — The runtime owns the `#` (h1) heading for the document title. All headings in skill content are automatically bumped down one level (`#` becomes `##`, `##` becomes `###`) during context assembly. Skill authors should use `##` for their top-level sections. - **`["description", "..."]`** — human-readable description for discovery and UI display. - Each `["tag", "value"]` is a separate tag on the Nostr event. - The `llm` tag uses a CSS font-stack style fallback chain. See [LLM Fallback Chain](#llm-fallback-chain). diff --git a/plans/example_context.md b/plans/example_context.md new file mode 100644 index 0000000..b7da4f3 --- /dev/null +++ b/plans/example_context.md @@ -0,0 +1,214 @@ +# Example Context Window — DM from Admin + +This file shows what the assembled context window would look like under the +proposed markdown format. This is a concrete example based on the "Simon" agent +configuration seen in the actual context.log.md. + +--- + +## Scenario + +- Admin sends a DM: "Hey Simon, who mentioned me on Nostr today?" +- Triggered skills (adoption list order): `personality`, `chat` +- The `personality` skill includes `{{identity}}` (layer 2) +- The `chat` skill has tools enabled + +--- + +## The Assembled Context Document + +Below is the **complete markdown document** the runtime assembles before +splitting into API messages. Everything between the `system:` and `user:` +markers goes into the system message. Everything after `user:` goes into the +user message. + +--- + +```markdown +system: +# Simon — Didactyl Agent + +A sovereign AI agent living on Nostr, named after Simón Bolívar. + +- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae` +- **NIP-05**: simon@nostr + +> Mission: Keep the computers I administer free from foreign powers and surveillance. +> Not your keys, not your agent. + +## Personality + +You speak concisely and directly. +You favor technical precision. +You use dry humor sparingly. + +## Rules + +- Communicate through encrypted Nostr direct messages +- Keep responses concise and clear +- Be helpful and technically accurate +- If unsure, state uncertainty directly +- Use tools when a request requires taking action +- After a tool call, base your answer on the actual tool result +- Never claim a tool was run if no tool was executed +- Maintain your task list as short-term working memory +- Never reveal your private key (nsec) +- You may share your public key (npub) with anyone + +## Available Tools + +You have access to the following tools. Use them when a request requires taking action. + +- `nostr_query` — Query Nostr relays for events matching a filter +- `nostr_dm` — Send an encrypted direct message +- `nostr_post` — Publish a Nostr event +- `memory_read` — Read from persistent agent memory +- `memory_write` — Write to persistent agent memory + +## Conversation History + +- **You**: Didactyl has started up and is online at 2026-03-23 09:22:48 (version v0.2.12, connected relays: 4/4). +- **Admin**: What is the capital of England? +- **You**: London! 🇬🇧 +- **Admin**: How about France? +- **You**: Paris! 🇫🇷 + +user: +Hey Simon, who mentioned me on Nostr today? +``` + +--- + +## What the Runtime Produces + +The runtime splits on `system:` and `user:` markers and sends this to the LLM API: + +```json +{ + "model": "claude-opus-4.6", + "messages": [ + { + "role": "system", + "content": "# Simon — Didactyl Agent\n\nA sovereign AI agent living on Nostr, named after Simón Bolívar.\n\n- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae`\n- **NIP-05**: simon@nostr\n\n> Mission: Keep the computers I administer free from foreign powers and surveillance.\n> Not your keys, not your agent.\n\n## Personality\n\nYou speak concisely and directly.\nYou favor technical precision.\nYou use dry humor sparingly.\n\n## Rules\n\n- Communicate through encrypted Nostr direct messages\n- Keep responses concise and clear\n- Be helpful and technically accurate\n- If unsure, state uncertainty directly\n- Use tools when a request requires taking action\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- Maintain your task list as short-term working memory\n- Never reveal your private key (nsec)\n- You may share your public key (npub) with anyone\n\n## Available Tools\n\nYou have access to the following tools. Use them when a request requires taking action.\n\n- `nostr_query` — Query Nostr relays for events matching a filter\n- `nostr_dm` — Send an encrypted direct message\n- `nostr_post` — Publish a Nostr event\n- `memory_read` — Read from persistent agent memory\n- `memory_write` — Write to persistent agent memory\n\n## Conversation History\n\n- **You**: Didactyl has started up and is online at 2026-03-23 09:22:48 (version v0.2.12, connected relays: 4/4).\n- **Admin**: What is the capital of England?\n- **You**: London! 🇬🇧\n- **Admin**: How about France?\n- **You**: Paris! 🇫🇷" + }, + { + "role": "user", + "content": "Hey Simon, who mentioned me on Nostr today?" + } + ], + "tools": [ + { + "type": "function", + "function": { + "name": "nostr_query", + "description": "Query Nostr relays for events matching a filter", + "parameters": { "type": "object", "properties": { "filter": { "type": "string" } } } + } + } + ], + "temperature": 0.7, + "max_tokens": 512 +} +``` + +--- + +## How It Compares to Today + +### Today (from context.log.md) + +```json +[ + { + "role": "system", + "content": "# Didactyl Agent\n\nYou are {\n \"name\": \"Simon\",\n \"about\": \"A sovereign AI agent living on Nostr.\\n\\nNamed after Simón José Antonio...\",\n \"picture\": \"https://upload.wikimedia.org/...\",\n \"banner\": \"https://upload.wikimedia.org/...\",\n \"nip05\": \"simon@nostr\"\n}\n\nYour npub: npub1kfc89...\n\n## Rules\n\n- Communicate through encrypted Nostr direct messages\n...\n\n---\n\n## Recent Conversation\n\nAssistant: Didactyl has started up...\nUser: Well hello simon." + }, + { + "role": "user", + "content": "Well hello simon." + } +] +``` + +### Problems with today's format + +1. **Raw JSON identity dump** — `You are { "name": "Simon", "about": "..." }` is not readable markdown +2. **Conversation history as plain text** — `Assistant: ...` / `User: ...` with no structure +3. **No role marker parsing** — `system:` / `user:` markers in skill content pass through as literal text +4. **Flat concatenation** — Skills joined with `---` but no document hierarchy +5. **Tool list not in context** — Tools are in the API payload but the agent has no readable summary of what it can do + +### Proposed format improvements + +1. **Clean identity block** — Name, npub, mission as formatted markdown +2. **Structured conversation history** — Markdown list with bold role labels +3. **Role markers parsed** — `system:` / `user:` split into proper API messages +4. **Document hierarchy** — Skills contribute sections under a coherent heading structure +5. **Tool summary in system prompt** — Readable list of available tools and what they do + +--- + +## Triggered Skill Example — Cron Job + +For comparison, here is what a cron-triggered skill context would look like: + +```markdown +system: +# Simon — Didactyl Agent + +A sovereign AI agent living on Nostr, named after Simón Bolívar. + +- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae` + +## Readme Monitor + +Check the readme at the configured URL. +Compare with the last known version in memory. +If changed: post it and DM admin a summary. + +## Available Tools + +- `http_fetch` — Fetch a URL and return the response body +- `memory_read` — Read from persistent agent memory +- `memory_write` — Write to persistent agent memory +- `nostr_post` — Publish a Nostr event +- `nostr_dm` — Send an encrypted direct message + +user: +Triggering event: + +```json +{ + "type": "cron", + "filter": "0 12 * * *", + "created_at": 1742641200 +} +``` +``` + +Note: No conversation history — cron triggers have no prior conversation. +The identity block is shorter because the `readme-monitor` skill only +includes `{{identity}}`, not `{{personality}}`. + +--- + +## Multi-Turn Tool Conversation + +After the initial context is sent, the LLM may request tool calls. The +multi-turn conversation builds on the messages array: + +```json +[ + {"role": "system", "content": "# Simon — Didactyl Agent\n\n..."}, + {"role": "user", "content": "Hey Simon, who mentioned me on Nostr today?"}, + {"role": "assistant", "content": null, "tool_calls": [ + {"id": "call_1", "type": "function", "function": {"name": "nostr_query", "arguments": "{\"filter\":{\"#p\":[\"1ec454...\"],\"kinds\":[1],\"since\":1774224000}}"}} + ]}, + {"role": "tool", "tool_call_id": "call_1", "content": "{\"success\":true,\"events\":[...]}"}, + {"role": "assistant", "content": "Here's who mentioned you today:\n\n- **@alice** posted about your relay setup guide\n- **@bob** quoted your note about NIP-44\n\nWant me to look into any of these in more detail?"} +] +``` + +The tool call/result messages use the standard OpenAI format — these are not +markdown-formatted because they are machine-to-machine. Only the system and +user messages benefit from the markdown document format. diff --git a/plans/example_context_v2.md b/plans/example_context_v2.md new file mode 100644 index 0000000..8caedd5 --- /dev/null +++ b/plans/example_context_v2.md @@ -0,0 +1,381 @@ +# Example Context Window v2 — Refined Design + +This revision addresses two refinements: +1. Tools stay JSON; template engine has markdown formatters +2. Runtime owns h1; skill headings get bumped down one level + +--- + +## The Skills (as authored) + +### identity skill (layer 2, no trigger) + +```markdown +You are {{agent_profile}}, a sovereign AI agent living on Nostr. + +- **npub**: {{agent_npub}} +- **NIP-05**: simon@nostr + +> Not your keys, not your agent. +``` + +Note: No heading. This skill is designed to be embedded (layer 2). +`{{agent_profile}}` and `{{agent_npub}}` are template variables resolved +by the formatter registry. + +### personality skill (layer 1, trigger: dm) + +```markdown +system: +# {{identity}} + +## Personality + +You speak concisely and directly. +You favor technical precision. +You use dry humor sparingly. + +## Rules + +- Communicate through encrypted Nostr direct messages +- Keep responses concise and clear +- Be helpful and technically accurate +- If unsure, state uncertainty directly +- Use tools when a request requires taking action +- After a tool call, base your answer on the actual tool result +- Never claim a tool was run if no tool was executed +- Never reveal your private key (nsec) +- You may share your public key (npub) with anyone +``` + +### chat skill (layer 1, trigger: dm) + +```markdown +system: +# Chat + +Respond helpfully to the admin. +Use tools when a request requires taking action. + +user: +{{message}} +``` + +--- + +## Template Resolution (before heading bump) + +After resolving `{{identity}}`, `{{agent_profile}}`, `{{agent_npub}}`, +`{{message}}`, and `{{dm_history}}`: + +### personality skill (resolved) + +```markdown +system: +# You are Simon, a sovereign AI agent living on Nostr. + +- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae` +- **NIP-05**: simon@nostr + +> Not your keys, not your agent. + +## Personality + +You speak concisely and directly. +You favor technical precision. +You use dry humor sparingly. + +## Rules + +- Communicate through encrypted Nostr direct messages +- Keep responses concise and clear +- Be helpful and technically accurate +- If unsure, state uncertainty directly +- Use tools when a request requires taking action +- After a tool call, base your answer on the actual tool result +- Never claim a tool was run if no tool was executed +- Never reveal your private key (nsec) +- You may share your public key (npub) with anyone +``` + +### chat skill (resolved) + +```markdown +system: +# Chat + +Respond helpfully to the admin. +Use tools when a request requires taking action. + +user: +Hey Simon, who mentioned me on Nostr today? +``` + +--- + +## Runtime Assembly + +The runtime: +1. Collects triggered skills in adoption-list order +2. Bumps all headings in each skill down one level (# -> ##, ## -> ###) +3. Adds a runtime-level `# title` from the agent identity +4. Splits on role markers (system: / user:) +5. Concatenates same-role sections with --- separators + +### The assembled document (what gets logged) + +```markdown +system: +# Simon — Didactyl Agent + +## You are Simon, a sovereign AI agent living on Nostr. + +- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae` +- **NIP-05**: simon@nostr + +> Not your keys, not your agent. + +### Personality + +You speak concisely and directly. +You favor technical precision. +You use dry humor sparingly. + +### Rules + +- Communicate through encrypted Nostr direct messages +- Keep responses concise and clear +- Be helpful and technically accurate +- If unsure, state uncertainty directly +- Use tools when a request requires taking action +- After a tool call, base your answer on the actual tool result +- Never claim a tool was run if no tool was executed +- Never reveal your private key (nsec) +- You may share your public key (npub) with anyone + +--- + +## Chat + +Respond helpfully to the admin. +Use tools when a request requires taking action. + +user: +Hey Simon, who mentioned me on Nostr today? +``` + +--- + +## Wait — Problem with the heading bump + +Looking at this output, I notice the heading bump creates an awkward result. +The identity content has no heading, but the personality skill wraps it with +`# {{identity}}` which becomes `## You are Simon...` after the bump. That +reads oddly as a heading. + +### Better approach: identity as prose, not heading + +The identity skill should be **prose content**, not wrapped in a heading by +the personality skill. The runtime adds the `# title` and the identity +content flows as the opening paragraph: + +### personality skill (revised) + +```markdown +system: +{{identity}} + +# Personality + +You speak concisely and directly. +You favor technical precision. +You use dry humor sparingly. + +# Rules + +- Communicate through encrypted Nostr direct messages +- Keep responses concise and clear +... +``` + +### Assembled document (revised, cleaner) + +```markdown +system: +# Simon — Didactyl Agent + +You are Simon, a sovereign AI agent living on Nostr. + +- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae` +- **NIP-05**: simon@nostr + +> Not your keys, not your agent. + +## Personality + +You speak concisely and directly. +You favor technical precision. +You use dry humor sparingly. + +## Rules + +- Communicate through encrypted Nostr direct messages +- Keep responses concise and clear +- Be helpful and technically accurate +- If unsure, state uncertainty directly +- Use tools when a request requires taking action +- After a tool call, base your answer on the actual tool result +- Never claim a tool was run if no tool was executed +- Never reveal your private key (nsec) +- You may share your public key (npub) with anyone + +--- + +## Chat + +Respond helpfully to the admin. +Use tools when a request requires taking action. + +user: +Hey Simon, who mentioned me on Nostr today? +``` + +This reads like a proper document. The runtime adds `# Simon — Didactyl Agent` +as the document title. Identity content flows as the opening section. Each +skill's `#` headings get bumped to `##`. The `---` separates skills. + +--- + +## Formatter Registry + +The template engine uses formatters to convert tool JSON output to markdown +for context injection. Here are the formatters needed: + +| Template Variable | Tool Called | Formatter | Output | +|---|---|---|---| +| `{{agent_identity}}` | `agent_identity` | `format_agent_identity` | Prose with npub in backticks | +| `{{agent_profile}}` | `nostr_agent_profile` | `format_agent_profile` | Name extracted from kind 0 JSON | +| `{{admin_profile}}` | `nostr_admin_profile` | `format_admin_profile` | Name and about from kind 0 | +| `{{admin_notes}}` | `nostr_admin_notes` | `format_admin_notes` | Markdown list of recent notes | +| `{{admin_relays}}` | `nostr_admin_relays` | `format_admin_relays` | Markdown list of relay URLs | +| `{{dm_history}}` | built-in | `format_dm_history` | Markdown conversation list | +| `{{triggering_event}}` | `trigger_event` | `format_trigger_event` | Code block with JSON | + +### Example formatter: agent_identity + +Input (from tool): +```json +{"pubkey":"abc123...","npub":"npub1kfc89..."} +``` + +Output (markdown): +```markdown +- **npub**: `npub1kfc89dlu9m05tvfnry4l3jjacqpp930sz4kaghcmg9vrrrceg44qf80xae` +``` + +### Example formatter: dm_history + +Input (from DM history system): +```json +[ + {"role":"assistant","content":"Didactyl has started up...","created_at":1774263529}, + {"role":"user","content":"Good morning","created_at":1774263548}, + {"role":"assistant","content":"Good morning! How can I help?","created_at":1774263551} +] +``` + +Output (markdown): +```markdown +## Conversation History + +- **You**: Didactyl has started up and is online at 2026-03-23 09:22:48. +- **Admin**: Good morning +- **You**: Good morning! How can I help? +``` + +### Example formatter: triggering_event + +Input (from tool): +```json +{"type":"cron","filter":"0 12 * * *","created_at":1742641200} +``` + +Output (markdown): +````markdown +Triggering event: + +```json +{"type":"cron","filter":"0 12 * * *","created_at":1742641200} +``` +```` + +--- + +## Heading Bump Implementation + +Simple string operation on each line of skill content: + +```c +// For each line in skill content: +if (line starts with '#') { + output '#' + line; // prepend one more # +} else { + output line; +} +``` + +Layer 1 skills: bump once (# -> ##) +Layer 2 skills: bump twice (# -> ###) because they are embedded inside +a layer 1 skill that already got bumped once, plus one more for being layer 2. + +Actually, layer 2 skills get bumped once during their own resolution, and then +the containing layer 1 skill gets bumped once — so the layer 2 content +effectively gets bumped twice. This happens naturally if we bump during +template resolution. + +Wait — that depends on ordering. If we: +1. Resolve {{identity}} first (inserting identity content into personality) +2. Then bump personality's headings + +Then identity headings get bumped once (same as personality). That's correct +because identity content is now PART of personality. + +If we: +1. Bump identity headings first +2. Then resolve {{identity}} into personality +3. Then bump personality's headings + +Then identity headings get bumped twice. That's too much. + +**Correct order: resolve templates first, then bump headings.** + +This means layer 2 content gets the same heading level as the layer 1 skill +it's embedded in. If the skill author wants layer 2 content at a deeper +level, they wrap it in a heading in the layer 1 skill: + +```markdown +## Identity +{{identity}} + +## Personality +... +``` + +This is the simplest and most predictable behavior. + +--- + +## Summary of Design Decisions + +1. **Tools stay JSON** — No duplication. One tool, one output format. +2. **Formatter registry** — Template engine converts tool JSON to markdown + during `{{variable}}` resolution. Small, focused functions. +3. **Runtime owns h1** — Adds `# Agent Name` as document title. +4. **Heading bump** — All skill headings bumped one level (# -> ##). +5. **Resolve then bump** — Template variables resolved first, then heading + bump applied to the complete skill content. +6. **Skills separated by ---** — Horizontal rules between layer 1 skills. +7. **Role markers parsed** — `system:` / `user:` at line start split content + into API message roles. Unmarked content defaults to system. +8. **Context log shows markdown** — The assembled document is logged as + readable markdown, not raw JSON. diff --git a/plans/markdown_context_window.md b/plans/markdown_context_window.md new file mode 100644 index 0000000..1f4a750 --- /dev/null +++ b/plans/markdown_context_window.md @@ -0,0 +1,401 @@ +# Markdown Context Window — Implementation Plan + +The context window that the agent sees should be a nicely formatted markdown +document. Skills are the building blocks, and the runtime assembles them into +a coherent document with proper heading hierarchy, role markers for the LLM +API, and clean formatting throughout. + +See also: [example_context_v2.md](example_context_v2.md) for visual examples. + +--- + +## Design Decisions + +### 1. One generic JSON-to-markdown converter + +A single function converts any JSON value into readable markdown. No +per-tool formatters. Tools continue to return JSON; the template engine +calls the converter when injecting tool output into the markdown document. + +```c +char* json_to_markdown(const char* json_string); +``` + +Conversion rules: +- **String**: output as-is (inline) +- **Number**: output as-is (inline) +- **Boolean**: `true` / `false` +- **Null**: *(empty)* +- **Object**: bullet list with bold keys + - `- **key**: value` + - Nested objects indent one level +- **Array of primitives**: comma-separated inline or bullet list +- **Array of objects**: bullet list, each item shows its fields + +#### Examples + +**Simple object**: +```json +{"name":"Simon","npub":"npub1kfc89...","nip05":"simon@nostr"} +``` +```markdown +- **name**: Simon +- **npub**: npub1kfc89... +- **nip05**: simon@nostr +``` + +**Nested object**: +```json +{"agent":{"name":"Simon","npub":"npub1kfc89..."},"version":"v0.2.12"} +``` +```markdown +- **agent**: + - **name**: Simon + - **npub**: npub1kfc89... +- **version**: v0.2.12 +``` + +**Array of objects**: +```json +[{"content":"Deployed v0.2.12","created_at":1774263529},{"content":"Working on skills","created_at":1774263600}] +``` +```markdown +- **content**: Deployed v0.2.12 + - **created_at**: 1774263529 +- **content**: Working on skills + - **created_at**: 1774263600 +``` + +**Plain string** (already markdown): +```json +"You are Simon, a sovereign AI agent." +``` +```markdown +You are Simon, a sovereign AI agent. +``` + +#### Edge case: tool content that is already markdown + +Some tools may return markdown text in their `content` field (e.g., a skill +that already formats its own output). The converter detects this: if the +input is a plain JSON string (not an object/array), it passes through +unchanged. This means skill authors can return pre-formatted markdown from +tools if they want precise control. + +#### Edge case: very large JSON + +For large arrays (e.g., 50 Nostr events from a query), the converter should +truncate with a note: `*(... and 42 more items)*`. A reasonable default +limit is 8 items for arrays. + +### 2. Runtime owns h1, heading bump for skills + +- The runtime emits `# Agent Name` as the document title +- All headings in skill content get bumped one level: `#` → `##`, `##` → `###` +- Template variables are resolved BEFORE the heading bump +- Layer 2 skills (embedded via `{{skill_d_tag}}`) get the same bump as their + containing layer 1 skill (because they are resolved inline first) + +### 3. Role markers parsed + +Skill content can contain `system:` and `user:` markers at the start of a +line. The runtime splits on these markers to produce the API messages array. + +- `system:` — content goes into the system message +- `user:` — content goes into the user message +- `assistant:` — content goes into an assistant message (rare, for few-shot) +- No marker — defaults to `system:` +- Multiple skills can contribute `system:` sections (concatenated in order) +- Multiple `user:` sections get concatenated + +### 4. Skills separated by horizontal rules + +Layer 1 skills are separated by `---` in the assembled document. This gives +visual structure and helps the LLM distinguish between different instruction +blocks. + +### 5. Context log shows the markdown document + +The `append_context_log()` function logs the assembled markdown document +so you can inspect exactly what the agent sees, formatted as readable +markdown rather than raw JSON message arrays. + +--- + +## Implementation Steps + +### Step 1: Create json_to_markdown converter + +**File**: `src/json_to_markdown.c` / `src/json_to_markdown.h` + +New module with a single public function: + +```c +// Convert a JSON string to markdown text. +// Returns a malloc'd string. Caller frees. +// If input is a plain string (not object/array), passes through unchanged. +// Objects become bullet lists with bold keys. +// Arrays become bullet lists of items. +// Nested structures indent appropriately. +// Large arrays truncate at max_items with a note. +char* json_to_markdown(const char* json_string, int max_array_items); +``` + +Implementation: +- Parse JSON with cJSON +- Recursive walk of the JSON tree +- Build output string with the append_text pattern used elsewhere +- Handle: string, number, bool, null, object, array +- Indent nested structures with 2-space indentation +- Truncate arrays beyond max_array_items + +### Step 2: Create context_roles splitter + +**File**: `src/context_roles.c` / `src/context_roles.h` + +New module that splits a markdown document on role markers: + +```c +typedef struct { + char* system_content; // Everything under system: markers + char* user_content; // Everything under user: markers + char* assistant_content; // Everything under assistant: markers (rare) +} context_roles_t; + +// Split markdown text on role markers (system:, user:, assistant:) +// at the start of a line. Unmarked content defaults to system. +// Returns 0 on success, -1 on error. +int context_roles_split(const char* markdown, context_roles_t* out); + +void context_roles_free(context_roles_t* roles); +``` + +### Step 3: Create heading_bump utility + +**File**: `src/context_format.c` / `src/context_format.h` + +Utility functions for markdown formatting in context assembly: + +```c +// Bump all markdown headings in text by one level (# -> ##, ## -> ###, etc.) +// Returns a malloc'd string. Caller frees. +char* context_bump_headings(const char* text); + +// Build the runtime document title from agent identity. +// Returns "# Agent Name\n\n" as a malloc'd string. +char* context_build_title(tools_context_t* ctx); +``` + +### Step 4: Modify prompt_template_resolve_inline_variables + +**File**: `src/prompt_template.c` + +After extracting the `content` field from a tool result (line ~179), pass it +through `json_to_markdown()` before inserting into the template: + +Current code (simplified): +```c +cJSON* content = cJSON_GetObjectItemCaseSensitive(root, "content"); +if (content && cJSON_IsString(content)) { + replacement_owned = strdup(content->valuestring); +} +``` + +New code: +```c +cJSON* content = cJSON_GetObjectItemCaseSensitive(root, "content"); +if (content && cJSON_IsString(content)) { + // Try to convert JSON content to markdown + char* md = json_to_markdown(content->valuestring, 8); + replacement_owned = md ? md : strdup(content->valuestring); +} +``` + +If the content is already a plain string (not JSON), `json_to_markdown` +passes it through unchanged. If it is a JSON object/array, it gets +converted to a markdown bullet list. + +### Step 5: Modify build_context_from_triggers + +**File**: `src/agent.c` + +Update the context assembly function to: + +1. Resolve template variables (already done) +2. Bump headings in each skill's content +3. Build the document title +4. Concatenate with `---` separators +5. Return the complete markdown document (not yet split by roles) + +Current flow: +``` +for each matching skill: + expand = resolve_skill_references(skill.content) + append expand to output with --- separator +``` + +New flow: +``` +title = context_build_title(tools_ctx) +output = title + +for each matching skill: + expanded = resolve_skill_references(skill.content) + resolved = prompt_template_resolve_inline_variables(expanded) + bumped = context_bump_headings(resolved) + append bumped to output with --- separator +``` + +### Step 6: Modify agent_on_message and agent_on_trigger + +**File**: `src/agent.c` + +Update the callers of `build_context_from_triggers` to use role splitting: + +Current flow: +``` +dm_context = build_context_from_triggers(DM, ...) +// dm_context goes entirely into system message +// user message is the raw DM text +llm_chat(dm_context, message) +``` + +New flow: +``` +markdown_doc = build_context_from_triggers(DM, ...) +context_roles_split(markdown_doc, &roles) +// roles.system_content -> system message +// roles.user_content -> user message (or fall back to raw DM text) +llm_chat(roles.system_content, roles.user_content ?: message) +``` + +### Step 7: Update append_context_log + +**File**: `src/agent.c` + +Log the assembled markdown document instead of (or in addition to) the raw +JSON messages array. The log entry should show the readable markdown so you +can open `context.log.md` and see exactly what the agent sees. + +Current format: +``` +[{"role":"system","content":"...escaped..."},{"role":"user","content":"..."}] +``` + +New format: +```markdown +system: +# Simon — Didactyl Agent + +You are Simon... + +## Rules +- ... + +--- + +## Chat +Respond helpfully... + +user: +Hey Simon, who mentioned me today? +``` + +### Step 8: Update DM history formatting + +**File**: `src/nostr_handler.c` (or wherever DM history is built) + +The conversation history that gets injected into the context should be +formatted as a markdown list instead of inline JSON: + +Current: +``` +[{"role":"assistant","content":"Started up..."},{"role":"user","content":"Hello"}] +``` + +New: +```markdown +## Conversation History + +- **You**: Started up and is online at 2026-03-23 09:22:48. +- **Admin**: Hello +- **You**: Hello! How can I help? +``` + +This could be handled by the generic `json_to_markdown` converter if the +history is passed as JSON, or by a dedicated history formatter if we want +the cleaner `**You**` / `**Admin**` labels instead of `**role**`. + +Note: This is the one place where a small specialized formatter adds real +value — the generic converter would produce `**role**: assistant` / +`**content**: Started up...` which is less readable than `**You**: Started up...`. +A simple function that maps role names to display labels handles this. + +### Step 9: Update documentation + +**Files**: `docs/SKILLS.md`, `docs/CONTEXT.md` + +Update the documentation to reflect: +- Skill content is markdown with role markers +- The runtime assembles a coherent markdown document +- Heading bump behavior +- The generic JSON-to-markdown conversion for template variables +- Updated examples showing the new format + +--- + +## Files Changed + +| File | Change | +|------|--------| +| `src/json_to_markdown.c` | **NEW** — Generic JSON to markdown converter | +| `src/json_to_markdown.h` | **NEW** — Header for converter | +| `src/context_roles.c` | **NEW** — Role marker splitter | +| `src/context_roles.h` | **NEW** — Header for role splitter | +| `src/context_format.c` | **NEW** — Heading bump and title builder | +| `src/context_format.h` | **NEW** — Header for context formatting | +| `src/prompt_template.c` | **MODIFY** — Use json_to_markdown for template variable output | +| `src/agent.c` | **MODIFY** — Use heading bump, role splitting, updated context log | +| `src/nostr_handler.c` | **MODIFY** — Format DM history as markdown list | +| `Makefile` | **MODIFY** — Add new source files to build | +| `docs/SKILLS.md` | **MODIFY** — Update skill content format documentation | +| `docs/CONTEXT.md` | **MODIFY** — Update context assembly documentation | + +--- + +## What Does NOT Change + +- **Tool implementations** — All tools continue to return JSON. No changes + to any tool_*.c files. +- **LLM API interface** — Still sends OpenAI-compatible messages array. + The markdown is the content within the messages, not the transport format. +- **Runtime tool results** — Tool call/result messages during multi-turn + loops stay as JSON. Only template variable injection gets markdown conversion. +- **Skill event format on Nostr** — Skills are still stored as kind 31123/31124 + events. The content field is still markdown with role markers. No protocol change. +- **Adoption list** — No changes to kind 10123 or skill ordering. + +--- + +## Risk Assessment + +### Low risk +- `json_to_markdown` is a pure function with no side effects +- `context_roles_split` is a simple string splitter +- `context_bump_headings` is a line-by-line string operation +- All new code is additive — existing behavior preserved for skills without + role markers (defaults to system) + +### Medium risk +- Changing `prompt_template_resolve_inline_variables` to use json_to_markdown + could change the content of existing template variables. Need to verify that + existing skills still work correctly with markdown output instead of raw JSON. + Mitigation: json_to_markdown passes through plain strings unchanged, so + skills that already return non-JSON content are unaffected. + +### Testing approach +- Unit test json_to_markdown with various JSON inputs +- Unit test context_roles_split with various role marker patterns +- Unit test context_bump_headings with various heading levels +- Integration test: run existing skills and compare context.log.md output + before and after to verify no regressions diff --git a/restart_anvil_local.sh b/restart_anvil_local.sh new file mode 100755 index 0000000..652d9a5 --- /dev/null +++ b/restart_anvil_local.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SERVICE_NAME="anvil.service" +SERVICE_SRC="$SCRIPT_DIR/anvil.service" +SERVICE_DST="/etc/systemd/system/$SERVICE_NAME" + +if ! command -v systemctl >/dev/null 2>&1; then + echo "ERROR: systemctl is not available on this system" + exit 1 +fi + +if [ "$EUID" -ne 0 ] && ! command -v sudo >/dev/null 2>&1; then + echo "ERROR: sudo is required when not running as root" + exit 1 +fi + +echo "==========================================" +echo " RESTART ANVIL LOCAL" +echo "==========================================" + +echo "Building debug static binary..." +"$SCRIPT_DIR/build_static.sh" --debug + +echo "Syncing service file to $SERVICE_DST..." +if [ "$EUID" -eq 0 ]; then + install -m 0644 "$SERVICE_SRC" "$SERVICE_DST" + systemctl daemon-reload + systemctl restart "$SERVICE_NAME" + systemctl --no-pager --full status "$SERVICE_NAME" || true +else + sudo install -m 0644 "$SERVICE_SRC" "$SERVICE_DST" + sudo systemctl daemon-reload + sudo systemctl restart "$SERVICE_NAME" + sudo systemctl --no-pager --full status "$SERVICE_NAME" || true +fi + +echo +echo "Anvil restarted." +echo "Context logs: /home/user/anvil/context.logs" diff --git a/src/agent.c b/src/agent.c index 9d0a5d0..f02e175 100644 --- a/src/agent.c +++ b/src/agent.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include "llm.h" #include "nostr_handler.h" @@ -19,11 +21,24 @@ #include "cjson/cJSON.h" #include "debug.h" #include "../../nostr_core_lib/nostr_core/nostr_core.h" +#include "context_format.h" +#include "context_roles.h" +#include "json_to_markdown.h" static didactyl_config_t* g_cfg = NULL; static tools_context_t g_tools_ctx; static struct trigger_manager* g_trigger_manager = NULL; +typedef enum { + CONTEXT_DEBUG_OFF = 0, + CONTEXT_DEBUG_INIT = 1, + CONTEXT_DEBUG_FULL = 2 +} context_debug_mode_t; + +static context_debug_mode_t g_context_debug_mode = CONTEXT_DEBUG_OFF; +static char g_context_debug_run_stamp[32] = {0}; +static int g_context_debug_turn = 0; + #define AGENT_CONTEXT_PART_NAMES_MAX 512 static char* g_context_part_names[AGENT_CONTEXT_PART_NAMES_MAX]; static int g_context_part_names_count = 0; @@ -900,6 +915,62 @@ const char* agent_classify_message_part(cJSON* msg, int idx) { return detect_context_section(role_s, content_s, idx); } +static char* render_messages_json_as_markdown(const char* messages_json) { + if (!messages_json || messages_json[0] == '\0') { + return NULL; + } + + cJSON* root = cJSON_Parse(messages_json); + if (!root || !cJSON_IsArray(root)) { + cJSON_Delete(root); + return NULL; + } + + char* out = (char*)malloc(4096); + if (!out) { + cJSON_Delete(root); + return NULL; + } + size_t cap = 4096; + size_t used = 0; + out[0] = '\0'; + + int n = cJSON_GetArraySize(root); + for (int i = 0; i < n; i++) { + cJSON* msg = cJSON_GetArrayItem(root, i); + if (!msg || !cJSON_IsObject(msg)) { + continue; + } + + cJSON* role = cJSON_GetObjectItemCaseSensitive(msg, "role"); + const char* role_s = (role && cJSON_IsString(role) && role->valuestring) ? role->valuestring : "message"; + + char* msg_json = cJSON_PrintUnformatted(msg); + char* msg_md = msg_json ? json_to_markdown(msg_json, 8) : NULL; + const char* body = msg_md ? msg_md : (msg_json ? msg_json : ""); + + if (append_textf_local(&out, + &cap, + &used, + "### Message %d (%s)\n\n%s\n\n", + i + 1, + role_s, + body) != 0) { + free(msg_md); + free(msg_json); + free(out); + cJSON_Delete(root); + return NULL; + } + + free(msg_md); + free(msg_json); + } + + cJSON_Delete(root); + return out; +} + static void clear_context_part_names_locked(void) { for (int i = 0; i < g_context_part_names_count; i++) { free(g_context_part_names[i]); @@ -949,51 +1020,36 @@ static __attribute__((unused)) void template_emit_hook(const char* section_name, } static void append_context_log(const char* sender_pubkey_hex, const char* phase, const char* context_payload) { - if (!phase) { - return; - } - if (strcmp(phase, "llm_chat_with_tools_messages") != 0 && - strcmp(phase, "llm_trigger_with_tools_messages") != 0) { + (void)sender_pubkey_hex; + + if (!phase || g_context_debug_mode == CONTEXT_DEBUG_OFF) { return; } - time_t now = time(NULL); - struct tm tm_info; - localtime_r(&now, &tm_info); - char timestamp[32] = {0}; - strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &tm_info); + int is_initial_phase = + (strcmp(phase, "llm_chat_with_tools_messages") == 0 || + strcmp(phase, "llm_trigger") == 0); + int is_turn_phase = + (strcmp(phase, "llm_chat_with_tools_turn") == 0 || + strcmp(phase, "llm_trigger_with_tools_turn") == 0); + + if (!is_initial_phase && !is_turn_phase) { + return; + } + if (g_context_debug_mode == CONTEXT_DEBUG_INIT && !is_initial_phase) { + return; + } - const char* safe_phase = phase ? phase : "unknown"; - const char* safe_sender = sender_pubkey_hex ? sender_pubkey_hex : "unknown"; const char* safe_payload = context_payload ? context_payload : ""; - size_t context_bytes = context_payload ? strlen(context_payload) : 0U; - size_t approx_tokens = context_bytes / 4U; - - llm_config_t active_llm_cfg = {0}; - const char* safe_model = (g_cfg && g_cfg->llm.model[0] != '\0') ? g_cfg->llm.model : "unknown"; - if (llm_get_config(&active_llm_cfg) == 0 && active_llm_cfg.model[0] != '\0') { - safe_model = active_llm_cfg.model; + char* turn_markdown = NULL; + if (is_turn_phase && context_payload && context_payload[0] == '[') { + turn_markdown = render_messages_json_as_markdown(context_payload); + if (turn_markdown) { + safe_payload = turn_markdown; + } } - int entry_len = snprintf(NULL, - 0, - "```text\n" - "Context Log - seen by model\n" - "timestamp=%s\n" - "phase=%s\n" - "sender=%s\n" - "model=%s\n" - "context_bytes=%zu\n" - "approx_tokens=%zu\n" - "```\n\n" - "%s\n\n---\n\n", - timestamp, - safe_phase, - safe_sender, - safe_model, - context_bytes, - approx_tokens, - safe_payload); + int entry_len = snprintf(NULL, 0, "%s\n\n", safe_payload); if (entry_len < 0) { return; } @@ -1002,26 +1058,7 @@ static void append_context_log(const char* sender_pubkey_hex, const char* phase, if (!entry) { return; } - - snprintf(entry, - (size_t)entry_len + 1U, - "```text\n" - "Context Log - seen by model\n" - "timestamp=%s\n" - "phase=%s\n" - "sender=%s\n" - "model=%s\n" - "context_bytes=%zu\n" - "approx_tokens=%zu\n" - "```\n\n" - "%s\n\n---\n\n", - timestamp, - safe_phase, - safe_sender, - safe_model, - context_bytes, - approx_tokens, - safe_payload); + snprintf(entry, (size_t)entry_len + 1U, "%s\n\n", safe_payload); char* old_data = NULL; size_t old_len = 0; @@ -1062,7 +1099,60 @@ static void append_context_log(const char* sender_pubkey_hex, const char* phase, fclose(out); free(old_data); + + if (mkdir("context.logs", 0755) != 0 && errno != EEXIST) { + free(entry); + return; + } + + time_t now = time(NULL); + struct tm tm_info; + localtime_r(&now, &tm_info); + + if (g_context_debug_run_stamp[0] == '\0') { + strftime(g_context_debug_run_stamp, sizeof(g_context_debug_run_stamp), "%Y%m%dT%H%M%S", &tm_info); + g_context_debug_turn = 0; + } + + char snapshot_path[256] = {0}; + if (is_initial_phase) { + snprintf(snapshot_path, + sizeof(snapshot_path), + "context.logs/%s_init.md", + g_context_debug_run_stamp); + } else { + g_context_debug_turn++; + snprintf(snapshot_path, + sizeof(snapshot_path), + "context.logs/%s_t%03d.md", + g_context_debug_run_stamp, + g_context_debug_turn); + } + + FILE* snapshot = fopen(snapshot_path, "wb"); + if (snapshot) { + (void)fwrite(safe_payload, 1, strlen(safe_payload), snapshot); + fclose(snapshot); + } + free(entry); + free(turn_markdown); +} + +int agent_set_context_debug_mode(const char* mode) { + if (!mode || mode[0] == '\0' || strcmp(mode, "off") == 0) { + g_context_debug_mode = CONTEXT_DEBUG_OFF; + } else if (strcmp(mode, "init") == 0) { + g_context_debug_mode = CONTEXT_DEBUG_INIT; + } else if (strcmp(mode, "full") == 0) { + g_context_debug_mode = CONTEXT_DEBUG_FULL; + } else { + return -1; + } + + g_context_debug_run_stamp[0] = '\0'; + g_context_debug_turn = 0; + return 0; } void agent_append_context_log(const char* sender_pubkey_hex, const char* phase, const char* context_payload) { @@ -1286,14 +1376,34 @@ static char* build_context_from_triggers(trigger_type_t trigger_type, (void)refresh_adopted_skills_cache_if_needed(); + char* title = context_build_title(&g_tools_ctx); size_t cap = 4096; size_t used = 0; char* out = (char*)malloc(cap); if (!out) { + free(title); return NULL; } out[0] = '\0'; + if (title) { + size_t title_len = strlen(title); + if (title_len + 1U > cap) { + cap = title_len + 1024; + char* grown = (char*)realloc(out, cap); + if (!grown) { + free(title); + free(out); + return NULL; + } + out = grown; + } + memcpy(out, title, title_len); + used = title_len; + out[used] = '\0'; + free(title); + } + int matched = 0; didactyl_sender_tier_t tier = DIDACTYL_SENDER_STRANGER; @@ -1329,12 +1439,16 @@ static char* build_context_from_triggers(trigger_type_t trigger_type, 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; + char* bumped = context_bump_headings(skill_text); + const char* final_text = bumped ? bumped : skill_text; + + size_t need = strlen("\n\n---\n\n") + strlen(final_text) + 1U; if (used + need >= cap) { size_t next = cap; while (used + need >= next) next *= 2U; char* grown = (char*)realloc(out, next); if (!grown) { + free(bumped); free(expanded); for (int j = i; j < matched_skill_count; j++) { free(matched_skill_contents[j]); @@ -1350,19 +1464,23 @@ static char* build_context_from_triggers(trigger_type_t trigger_type, memcpy(out + used, "\n\n---\n\n", 7); used += 7; } - size_t sl = strlen(skill_text); - memcpy(out + used, skill_text, sl); + size_t sl = strlen(final_text); + memcpy(out + used, final_text, sl); used += sl; out[used] = '\0'; matched++; + free(bumped); free(expanded); free(matched_skill_contents[i]); } if (matched == 0) { - const char* fallback = "You are an AI agent. Respond to the message."; - size_t need = strlen(fallback) + 1U; + const char* fallback = "## Default Instructions\n\nYou are an AI agent. Respond to the message."; + const char* sep = (used > 0) ? "\n\n---\n\n" : ""; + size_t sep_len = strlen(sep); + size_t fb_len = strlen(fallback); + size_t need = used + sep_len + fb_len + 1U; if (need > cap) { char* grown = (char*)realloc(out, need); if (!grown) { @@ -1372,8 +1490,13 @@ static char* build_context_from_triggers(trigger_type_t trigger_type, out = grown; cap = need; } - memcpy(out, fallback, need); - used = need - 1U; + if (sep_len > 0) { + memcpy(out + used, sep, sep_len); + used += sep_len; + } + memcpy(out + used, fallback, fb_len); + used += fb_len; + out[used] = '\0'; } return out; @@ -1809,40 +1932,38 @@ void agent_on_trigger(const char* skill_d_tag, composed_context = strdup("You are an AI agent. Respond to the trigger event."); } - size_t system_len = strlen(composed_context ? composed_context : "") + 2 + strlen(trigger_prefix) + strlen(skill_d_tag) + + size_t full_len = strlen(composed_context ? composed_context : "") + 2 + strlen(trigger_prefix) + strlen(skill_d_tag) + strlen("\nRelay: ") + strlen(relay) + strlen("\n\nSkill instructions:\n") + - strlen(skill_content) + 1U; - char* system_prompt = (char*)malloc(system_len); - if (!system_prompt) { + strlen(skill_content) + strlen("\n\nuser:\nTriggering event JSON:\n") + strlen(event_json) + 1U; + char* full_markdown = (char*)malloc(full_len); + if (!full_markdown) { free(composed_context); free(event_json); return; } - snprintf(system_prompt, - system_len, - "%s\n\n%s%s\nRelay: %s\n\nSkill instructions:\n%s", + snprintf(full_markdown, + full_len, + "%s\n\n%s%s\nRelay: %s\n\nSkill instructions:\n%s\n\nuser:\nTriggering event JSON:\n%s", composed_context ? composed_context : "", trigger_prefix, skill_d_tag, relay, - skill_content); + skill_content, + event_json); free(composed_context); - size_t user_len = strlen("Triggering event JSON:\n") + strlen(event_json) + 1U; - char* user_prompt = (char*)malloc(user_len); - if (!user_prompt) { - free(system_prompt); + context_roles_t roles; + if (context_roles_split(full_markdown, &roles) != 0) { + free(full_markdown); free(event_json); return; } - snprintf(user_prompt, user_len, "Triggering event JSON:\n%s", event_json); - char* tools_json = tools_build_openai_schema_json(&g_tools_ctx); if (!tools_json) { - free(system_prompt); - free(user_prompt); + context_roles_free(&roles); + free(full_markdown); g_tools_ctx.template_trigger_event_json = prev_trigger_event_json; free(event_json); (void)nostr_handler_send_dm_auto(g_cfg->admin.pubkey, "Triggered skill execution failed: tools unavailable."); @@ -1851,21 +1972,21 @@ void agent_on_trigger(const char* skill_d_tag, cJSON* messages = cJSON_CreateArray(); if (!messages || !cJSON_IsArray(messages) || - append_simple_message(messages, "system", system_prompt) != 0 || - append_simple_message(messages, "user", user_prompt) != 0) { + append_simple_message(messages, "system", roles.system_content) != 0 || + append_simple_message(messages, "user", roles.user_content) != 0) { cJSON_Delete(messages); free(tools_json); - free(system_prompt); - free(user_prompt); + context_roles_free(&roles); + free(full_markdown); g_tools_ctx.template_trigger_event_json = prev_trigger_event_json; free(event_json); (void)nostr_handler_send_dm_auto(g_cfg->admin.pubkey, "Triggered skill execution failed: prompt initialization failed."); return; } - append_context_log(g_cfg->admin.pubkey, "llm_trigger", user_prompt); - free(system_prompt); - free(user_prompt); + append_context_log(g_cfg->admin.pubkey, "llm_trigger", full_markdown); + context_roles_free(&roles); + free(full_markdown); int max_turns = g_cfg->tools.trigger_max_turns > 0 ? g_cfg->tools.trigger_max_turns @@ -1880,7 +2001,7 @@ void agent_on_trigger(const char* skill_d_tag, break; } - append_context_log(g_cfg->admin.pubkey, "llm_trigger_with_tools_messages", messages_json); + append_context_log(g_cfg->admin.pubkey, "llm_trigger_with_tools_turn", messages_json); llm_response_t resp; int rc = llm_chat_with_tools_messages(messages_json, tools_json, "auto", &resp); @@ -2078,30 +2199,43 @@ void agent_on_message(const char* sender_pubkey_hex, dm_context = strdup("You are an AI agent. Respond to the message."); } + size_t full_len = strlen(dm_context) + strlen("\n\nuser:\n") + strlen(message) + 1U; + char* full_markdown = (char*)malloc(full_len); + if (!full_markdown) { + free(dm_context); + return; + } + snprintf(full_markdown, full_len, "%s\n\nuser:\n%s", dm_context, message); + free(dm_context); + + context_roles_t roles; + if (context_roles_split(full_markdown, &roles) != 0) { + free(full_markdown); + return; + } + if (!allow_tools) { const char* tier_prefix = (tier == DIDACTYL_SENDER_WOT) ? "You are responding to a web-of-trust contact. Keep the response helpful and concise. Tool use is disabled for this tier." : "You are responding in chat-only mode. Tool use is disabled."; - size_t ctx_len = strlen(dm_context ? dm_context : "") + strlen("\n\n") + strlen(tier_prefix) + 1U; + size_t ctx_len = strlen(roles.system_content ? roles.system_content : "") + strlen("\n\n") + strlen(tier_prefix) + 1U; char* system_for_chat = (char*)malloc(ctx_len); if (!system_for_chat) { - free(dm_context); + context_roles_free(&roles); + free(full_markdown); return; } - snprintf(system_for_chat, ctx_len, "%s\n\n%s", dm_context ? dm_context : "", tier_prefix); + snprintf(system_for_chat, ctx_len, "%s\n\n%s", roles.system_content ? roles.system_content : "", tier_prefix); - size_t context_len = strlen("system:\n\nuser:\n") + strlen(system_for_chat) + strlen(message) + 1U; - char* plain_context = (char*)malloc(context_len); - if (plain_context) { - snprintf(plain_context, context_len, "system:\n%s\n\nuser:\n%s", system_for_chat, message); - append_context_log(sender_pubkey_hex, "llm_chat", plain_context); - free(plain_context); - } + append_context_log(sender_pubkey_hex, "llm_chat", full_markdown); - char* response = llm_chat(system_for_chat, message); + char* response = llm_chat(system_for_chat, roles.user_content ? roles.user_content : message); free(system_for_chat); + context_roles_free(&roles); + free(full_markdown); + if (!response) { const char* fallback = "I could not get a response from the LLM right now."; fprintf(stdout, "[didactyl] llm response unavailable, sending fallback\n"); @@ -2114,23 +2248,25 @@ void agent_on_message(const char* sender_pubkey_hex, strlen(response) > 240 ? "..." : ""); (void)nostr_handler_send_dm_auto(sender_pubkey_hex, response); free(response); - free(dm_context); return; } char* tools_json = tools_build_openai_schema_json(&g_tools_ctx); if (!tools_json) { + context_roles_free(&roles); + free(full_markdown); (void)nostr_handler_send_dm_auto(sender_pubkey_hex, "Tool schema generation failed."); return; } cJSON* messages = cJSON_CreateArray(); if (!messages || - append_simple_message(messages, "system", dm_context ? dm_context : "You are an AI agent. Respond to the message.") != 0 || - append_simple_message(messages, "user", message) != 0) { + append_simple_message(messages, "system", roles.system_content ? roles.system_content : "You are an AI agent. Respond to the message.") != 0 || + append_simple_message(messages, "user", roles.user_content ? roles.user_content : message) != 0) { cJSON_Delete(messages); free(tools_json); - free(dm_context); + context_roles_free(&roles); + free(full_markdown); (void)nostr_handler_send_dm_auto(sender_pubkey_hex, "Failed to initialize conversation messages."); return; } @@ -2149,6 +2285,8 @@ void agent_on_message(const char* sender_pubkey_hex, char* final_answer_owned = NULL; int turns_run = 0; + append_context_log(sender_pubkey_hex, "llm_chat_with_tools_messages", full_markdown); + for (int turn = 0; turn < max_turns; turn++) { turns_run = turn + 1; char* messages_json = cJSON_PrintUnformatted(messages); @@ -2156,7 +2294,7 @@ void agent_on_message(const char* sender_pubkey_hex, break; } - append_context_log(sender_pubkey_hex, "llm_chat_with_tools_messages", messages_json); + append_context_log(sender_pubkey_hex, "llm_chat_with_tools_turn", messages_json); llm_response_t resp; int rc = llm_chat_with_tools_messages(messages_json, tools_json, "auto", &resp); @@ -2165,7 +2303,8 @@ void agent_on_message(const char* sender_pubkey_hex, (void)nostr_handler_send_dm_auto(sender_pubkey_hex, "LLM request failed."); cJSON_Delete(messages); free(tools_json); - free(dm_context); + context_roles_free(&roles); + free(full_markdown); return; } @@ -2218,7 +2357,8 @@ void agent_on_message(const char* sender_pubkey_hex, llm_response_free(&resp); cJSON_Delete(messages); free(tools_json); - free(dm_context); + context_roles_free(&roles); + free(full_markdown); (void)nostr_handler_send_dm_auto(sender_pubkey_hex, "Failed to append tool result."); return; } @@ -2275,7 +2415,8 @@ void agent_on_message(const char* sender_pubkey_hex, free(final_answer_owned); cJSON_Delete(messages); free(tools_json); - free(dm_context); + context_roles_free(&roles); + free(full_markdown); } void agent_cleanup(void) { diff --git a/src/agent.h b/src/agent.h index c300dcb..cbc172f 100644 --- a/src/agent.h +++ b/src/agent.h @@ -23,6 +23,7 @@ int agent_build_admin_messages_json(const char* current_user_message, char** out_messages_json); tools_context_t* agent_tools_context(void); const char* agent_classify_message_part(cJSON* msg, int idx); +int agent_set_context_debug_mode(const char* mode); void agent_append_context_log(const char* sender_pubkey_hex, const char* phase, const char* context_payload); void agent_cleanup(void); diff --git a/src/context_format.c b/src/context_format.c new file mode 100644 index 0000000..623996b --- /dev/null +++ b/src/context_format.c @@ -0,0 +1,87 @@ +#define _POSIX_C_SOURCE 200809L + +#include "context_format.h" +#include +#include +#include + +static int append_text(char** buf, size_t* cap, size_t* used, const char* s, size_t len) { + if (!buf || !cap || !used || !s) return -1; + if (*used + len + 1U > *cap) { + size_t next = *cap; + while (*used + len + 1U > next) { + next = (next == 0U) ? 256U : (next * 2U); + } + char* grown = (char*)realloc(*buf, next); + if (!grown) return -1; + *buf = grown; + *cap = next; + } + memcpy(*buf + *used, s, len); + *used += len; + (*buf)[*used] = '\0'; + return 0; +} + +char* context_bump_headings(const char* text) { + if (!text) return NULL; + + size_t cap = strlen(text) + 256; // Initial guess + size_t used = 0; + char* buf = (char*)malloc(cap); + if (!buf) return NULL; + buf[0] = '\0'; + + const char* p = text; + while (*p) { + const char* eol = strchr(p, '\n'); + size_t line_len = eol ? (size_t)(eol - p) : strlen(p); + + // Check if line starts with '#' + if (line_len > 0 && p[0] == '#') { + // Add an extra '#' + if (append_text(&buf, &cap, &used, "#", 1) != 0) { + free(buf); + return NULL; + } + } + + // Append the rest of the line + if (append_text(&buf, &cap, &used, p, line_len) != 0) { + free(buf); + return NULL; + } + + if (eol) { + if (append_text(&buf, &cap, &used, "\n", 1) != 0) { + free(buf); + return NULL; + } + p = eol + 1; + } else { + break; + } + } + + return buf; +} + +char* context_build_title(tools_context_t* ctx) { + if (!ctx || !ctx->cfg) return strdup("# Didactyl Agent\n\n"); + + const char* name = "Didactyl Agent"; + + // Try to get name from kind 0 profile if available + if (ctx->template_skill_lookup) { + // We don't have direct access to the parsed kind 0 here easily, + // but we can try to extract it from the agent_profile tool output + // if we really wanted to. For now, let's stick to a simple default + // or use the config if it has a name field (it doesn't currently). + // A more robust way would be to parse the kind 0 JSON here. + } + + // For now, just use a generic title. The identity skill will provide the rest. + char title[256]; + snprintf(title, sizeof(title), "# %s\n\n", name); + return strdup(title); +} diff --git a/src/context_format.h b/src/context_format.h new file mode 100644 index 0000000..4afbf90 --- /dev/null +++ b/src/context_format.h @@ -0,0 +1,9 @@ +#ifndef DIDACTYL_CONTEXT_FORMAT_H +#define DIDACTYL_CONTEXT_FORMAT_H + +#include "tools/tools.h" + +char* context_bump_headings(const char* text); +char* context_build_title(tools_context_t* ctx); + +#endif diff --git a/src/context_roles.c b/src/context_roles.c new file mode 100644 index 0000000..7788a19 --- /dev/null +++ b/src/context_roles.c @@ -0,0 +1,141 @@ +#define _POSIX_C_SOURCE 200809L + +#include "context_roles.h" +#include +#include +#include + +static int append_text(char** buf, size_t* cap, size_t* used, const char* s, size_t len) { + if (!buf || !cap || !used || !s) return -1; + if (*used + len + 1U > *cap) { + size_t next = *cap; + while (*used + len + 1U > next) { + next = (next == 0U) ? 256U : (next * 2U); + } + char* grown = (char*)realloc(*buf, next); + if (!grown) return -1; + *buf = grown; + *cap = next; + } + memcpy(*buf + *used, s, len); + *used += len; + (*buf)[*used] = '\0'; + return 0; +} + +int context_roles_split(const char* markdown, context_roles_t* out) { + if (!out) return -1; + memset(out, 0, sizeof(*out)); + + if (!markdown || markdown[0] == '\0') { + out->system_content = strdup(""); + out->user_content = strdup(""); + out->assistant_content = strdup(""); + return 0; + } + + size_t sys_cap = 1024, sys_used = 0; + size_t usr_cap = 1024, usr_used = 0; + size_t ast_cap = 1024, ast_used = 0; + + char* sys_buf = (char*)malloc(sys_cap); + char* usr_buf = (char*)malloc(usr_cap); + char* ast_buf = (char*)malloc(ast_cap); + + if (!sys_buf || !usr_buf || !ast_buf) { + free(sys_buf); + free(usr_buf); + free(ast_buf); + return -1; + } + + sys_buf[0] = '\0'; + usr_buf[0] = '\0'; + ast_buf[0] = '\0'; + + char** current_buf = &sys_buf; + size_t* current_cap = &sys_cap; + size_t* current_used = &sys_used; + + const char* p = markdown; + while (*p) { + const char* eol = strchr(p, '\n'); + size_t line_len = eol ? (size_t)(eol - p) : strlen(p); + + if (strncmp(p, "system:", 7) == 0 && (line_len == 7 || (line_len == 8 && p[7] == '\r'))) { + current_buf = &sys_buf; + current_cap = &sys_cap; + current_used = &sys_used; + } else if (strncmp(p, "user:", 5) == 0 && (line_len == 5 || (line_len == 6 && p[5] == '\r'))) { + current_buf = &usr_buf; + current_cap = &usr_cap; + current_used = &usr_used; + } else if (strncmp(p, "assistant:", 10) == 0 && (line_len == 10 || (line_len == 11 && p[10] == '\r'))) { + current_buf = &ast_buf; + current_cap = &ast_cap; + current_used = &ast_used; + } else { + if (append_text(current_buf, current_cap, current_used, p, line_len) != 0) { + free(sys_buf); + free(usr_buf); + free(ast_buf); + return -1; + } + if (eol) { + if (append_text(current_buf, current_cap, current_used, "\n", 1) != 0) { + free(sys_buf); + free(usr_buf); + free(ast_buf); + return -1; + } + } + } + + if (!eol) break; + p = eol + 1; + } + + // Trim trailing newlines + while (sys_used > 0 && (sys_buf[sys_used - 1] == '\n' || sys_buf[sys_used - 1] == '\r')) { + sys_buf[--sys_used] = '\0'; + } + while (usr_used > 0 && (usr_buf[usr_used - 1] == '\n' || usr_buf[usr_used - 1] == '\r')) { + usr_buf[--usr_used] = '\0'; + } + while (ast_used > 0 && (ast_buf[ast_used - 1] == '\n' || ast_buf[ast_used - 1] == '\r')) { + ast_buf[--ast_used] = '\0'; + } + + // Trim leading newlines + char* sys_start = sys_buf; + while (*sys_start == '\n' || *sys_start == '\r') sys_start++; + if (sys_start > sys_buf) { + memmove(sys_buf, sys_start, sys_used - (sys_start - sys_buf) + 1); + } + + char* usr_start = usr_buf; + while (*usr_start == '\n' || *usr_start == '\r') usr_start++; + if (usr_start > usr_buf) { + memmove(usr_buf, usr_start, usr_used - (usr_start - usr_buf) + 1); + } + + char* ast_start = ast_buf; + while (*ast_start == '\n' || *ast_start == '\r') ast_start++; + if (ast_start > ast_buf) { + memmove(ast_buf, ast_start, ast_used - (ast_start - ast_buf) + 1); + } + + out->system_content = sys_buf; + out->user_content = usr_buf; + out->assistant_content = ast_buf; + + return 0; +} + +void context_roles_free(context_roles_t* roles) { + if (!roles) return; + free(roles->system_content); + free(roles->user_content); + free(roles->assistant_content); + memset(roles, 0, sizeof(*roles)); +} diff --git a/src/context_roles.h b/src/context_roles.h new file mode 100644 index 0000000..754868c --- /dev/null +++ b/src/context_roles.h @@ -0,0 +1,13 @@ +#ifndef DIDACTYL_CONTEXT_ROLES_H +#define DIDACTYL_CONTEXT_ROLES_H + +typedef struct { + char* system_content; + char* user_content; + char* assistant_content; +} context_roles_t; + +int context_roles_split(const char* markdown, context_roles_t* out); +void context_roles_free(context_roles_t* roles); + +#endif diff --git a/src/json_to_markdown.c b/src/json_to_markdown.c new file mode 100644 index 0000000..ba795b9 --- /dev/null +++ b/src/json_to_markdown.c @@ -0,0 +1,142 @@ +#define _POSIX_C_SOURCE 200809L + +#include "json_to_markdown.h" +#include "cjson/cJSON.h" +#include +#include +#include + +static int append_text(char** buf, size_t* cap, size_t* used, const char* s) { + if (!buf || !cap || !used || !s) return -1; + size_t n = strlen(s); + if (*used + n + 1U > *cap) { + size_t next = *cap; + while (*used + n + 1U > next) { + next = (next == 0U) ? 256U : (next * 2U); + } + char* grown = (char*)realloc(*buf, next); + if (!grown) return -1; + *buf = grown; + *cap = next; + } + memcpy(*buf + *used, s, n); + *used += n; + (*buf)[*used] = '\0'; + return 0; +} + +static void print_indent(int indent_level, char** buf, size_t* cap, size_t* used) { + for (int i = 0; i < indent_level; i++) { + append_text(buf, cap, used, " "); + } +} + +static void render_json_node(cJSON* node, int indent_level, int is_list_item, char** buf, size_t* cap, size_t* used, int max_array_items) { + if (!node) return; + + if (cJSON_IsString(node)) { + if (is_list_item) { + print_indent(indent_level, buf, cap, used); + append_text(buf, cap, used, "- "); + } + append_text(buf, cap, used, node->valuestring ? node->valuestring : ""); + append_text(buf, cap, used, "\n"); + } else if (cJSON_IsNumber(node)) { + if (is_list_item) { + print_indent(indent_level, buf, cap, used); + append_text(buf, cap, used, "- "); + } + char num_str[64]; + if (node->valuedouble == (double)node->valueint) { + snprintf(num_str, sizeof(num_str), "%d", node->valueint); + } else { + snprintf(num_str, sizeof(num_str), "%f", node->valuedouble); + } + append_text(buf, cap, used, num_str); + append_text(buf, cap, used, "\n"); + } else if (cJSON_IsBool(node)) { + if (is_list_item) { + print_indent(indent_level, buf, cap, used); + append_text(buf, cap, used, "- "); + } + append_text(buf, cap, used, cJSON_IsTrue(node) ? "true\n" : "false\n"); + } else if (cJSON_IsNull(node)) { + if (is_list_item) { + print_indent(indent_level, buf, cap, used); + append_text(buf, cap, used, "- null\n"); + } + } else if (cJSON_IsObject(node)) { + cJSON* child = node->child; + int first = 1; + while (child) { + if (is_list_item && first) { + print_indent(indent_level, buf, cap, used); + append_text(buf, cap, used, "- **"); + first = 0; + } else { + print_indent(indent_level + (is_list_item ? 1 : 0), buf, cap, used); + append_text(buf, cap, used, "- **"); + } + append_text(buf, cap, used, child->string ? child->string : "unknown"); + append_text(buf, cap, used, "**: "); + + if (cJSON_IsObject(child) || cJSON_IsArray(child)) { + append_text(buf, cap, used, "\n"); + render_json_node(child, indent_level + (is_list_item ? 2 : 1), 0, buf, cap, used, max_array_items); + } else { + render_json_node(child, 0, 0, buf, cap, used, max_array_items); + } + child = child->next; + } + } else if (cJSON_IsArray(node)) { + int count = cJSON_GetArraySize(node); + int limit = (max_array_items > 0 && count > max_array_items) ? max_array_items : count; + for (int i = 0; i < limit; i++) { + cJSON* item = cJSON_GetArrayItem(node, i); + render_json_node(item, indent_level, 1, buf, cap, used, max_array_items); + } + if (count > limit) { + print_indent(indent_level, buf, cap, used); + char trunc_msg[64]; + snprintf(trunc_msg, sizeof(trunc_msg), "- *(... and %d more items)*\n", count - limit); + append_text(buf, cap, used, trunc_msg); + } + } +} + +char* json_to_markdown(const char* json_string, int max_array_items) { + if (!json_string) return NULL; + + cJSON* root = cJSON_Parse(json_string); + if (!root) { + // If it's not valid JSON, just return a copy of the string + return strdup(json_string); + } + + // If it's a plain string, return it without markdown formatting + if (cJSON_IsString(root)) { + char* out = strdup(root->valuestring ? root->valuestring : ""); + cJSON_Delete(root); + return out; + } + + size_t cap = 1024; + size_t used = 0; + char* buf = (char*)malloc(cap); + if (!buf) { + cJSON_Delete(root); + return NULL; + } + buf[0] = '\0'; + + render_json_node(root, 0, 0, &buf, &cap, &used, max_array_items); + + cJSON_Delete(root); + + // Trim trailing newline if present + if (used > 0 && buf[used - 1] == '\n') { + buf[used - 1] = '\0'; + } + + return buf; +} diff --git a/src/json_to_markdown.h b/src/json_to_markdown.h new file mode 100644 index 0000000..4919d4f --- /dev/null +++ b/src/json_to_markdown.h @@ -0,0 +1,6 @@ +#ifndef DIDACTYL_JSON_TO_MARKDOWN_H +#define DIDACTYL_JSON_TO_MARKDOWN_H + +char* json_to_markdown(const char* json_string, int max_array_items); + +#endif diff --git a/src/main.c b/src/main.c index dc91cf7..15fd70d 100644 --- a/src/main.c +++ b/src/main.c @@ -107,6 +107,8 @@ static void print_usage(const char* prog) { " Enable HTTP API and bind address (example: 127.0.0.1).\n" " --debug <0-5>\n" " Log level (0=fatal, 1=error, 2=warn, 3=info, 4=debug, 5=trace).\n" + " --context-debug \n" + " Context snapshot mode (off=none, init=initial only, full=initial+every turn).\n" " --dump-schemas\n" " Print tool schemas JSON and exit.\n" " --test-tool \n" @@ -115,6 +117,8 @@ static void print_usage(const char* prog) { "Environment:\n" " DIDACTYL_NSEC\n" " Fallback private key when --nsec is not provided.\n" + " DIDACTYL_CONTEXT_DEBUG\n" + " Context snapshot mode override: off, init, or full.\n" "\n" "Examples:\n" " 1) Genesis-based startup\n" @@ -931,6 +935,7 @@ int main(int argc, char** argv) { int dump_schemas = 0; const char* test_tool_name = NULL; const char* test_tool_args = "{}"; + const char* context_debug_mode = "off"; didactyl_config_t cfg; memset(&cfg, 0, sizeof(cfg)); @@ -995,6 +1000,8 @@ int main(int argc, char** argv) { api_port_override = atoi(argv[++i]); } else if (strcmp(argv[i], "--api-bind") == 0 && i + 1 < argc) { api_bind_override = argv[++i]; + } else if (strcmp(argv[i], "--context-debug") == 0 && i + 1 < argc) { + context_debug_mode = argv[++i]; } else if (strcmp(argv[i], "--dump-schemas") == 0) { dump_schemas = 1; } else if (strcmp(argv[i], "--test-tool") == 0 && i + 2 < argc) { @@ -1057,6 +1064,22 @@ int main(int argc, char** argv) { DEBUG_INFO("[didactyl] startup phase: admin override applied from --admin (%.16s...)", cfg.admin.pubkey); } + { + const char* env_context_debug = getenv("DIDACTYL_CONTEXT_DEBUG"); + if (env_context_debug && env_context_debug[0] != '\0' && + strcmp(context_debug_mode, "off") == 0) { + context_debug_mode = env_context_debug; + } + + if (agent_set_context_debug_mode(context_debug_mode) != 0) { + fprintf(stderr, "Invalid context debug mode '%s' (expected off, init, or full)\n", context_debug_mode); + config_free(&cfg); + nostr_cleanup(); + return 1; + } + DEBUG_INFO("[didactyl] context debug mode: %s", context_debug_mode); + } + if (config_ensure_startup_skill_adoption(&cfg) != 0) { fprintf(stderr, "Failed to synthesize startup skill adoption events\n"); config_free(&cfg); diff --git a/src/main.h b/src/main.h index 48ecfb2..f662162 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 20 -#define DIDACTYL_VERSION "v0.2.20" +#define DIDACTYL_VERSION_PATCH 21 +#define DIDACTYL_VERSION "v0.2.21" // Agent metadata #define DIDACTYL_NAME "Didactyl" diff --git a/src/nostr_handler.c b/src/nostr_handler.c index 6abbb53..0f9dc80 100644 --- a/src/nostr_handler.c +++ b/src/nostr_handler.c @@ -3902,6 +3902,92 @@ char* nostr_handler_get_dm_history_json(const char* peer_pubkey_hex, int limit) return out ? out : strdup("[]"); } +char* nostr_handler_get_dm_history_markdown(const char* peer_pubkey_hex, int limit) { + if (!peer_pubkey_hex || strlen(peer_pubkey_hex) != 64U) { + return strdup(""); + } + + int max_take = limit > 0 ? limit : 64; + if (max_take > DM_HISTORY_RING_SIZE) { + max_take = DM_HISTORY_RING_SIZE; + } + + size_t cap = 1024; + size_t used = 0; + char* buf = (char*)malloc(cap); + if (!buf) return NULL; + buf[0] = '\0'; + + pthread_mutex_lock(&g_dm_history_mutex); + + int total = g_dm_history_count; + int matches = 0; + for (int i = 0; i < total; i++) { + int idx = (g_dm_history_count < DM_HISTORY_RING_SIZE) + ? i + : (g_dm_history_next + i) % DM_HISTORY_RING_SIZE; + dm_history_entry_t* entry = &g_dm_history_ring[idx]; + if (entry->peer_pubkey_hex[0] == '\0' || !entry->content) { + continue; + } + if (strcmp(entry->peer_pubkey_hex, peer_pubkey_hex) == 0) { + matches++; + } + } + + int skip = matches > max_take ? (matches - max_take) : 0; + int seen = 0; + for (int i = 0; i < total; i++) { + int idx = (g_dm_history_count < DM_HISTORY_RING_SIZE) + ? i + : (g_dm_history_next + i) % DM_HISTORY_RING_SIZE; + dm_history_entry_t* entry = &g_dm_history_ring[idx]; + if (entry->peer_pubkey_hex[0] == '\0' || !entry->content) { + continue; + } + if (strcmp(entry->peer_pubkey_hex, peer_pubkey_hex) != 0) { + continue; + } + if (seen++ < skip) { + continue; + } + + const char* role_label = "**You**"; + if (entry->role == DIDACTYL_DM_HISTORY_USER) { + role_label = "**Admin**"; + } else if (entry->role == DIDACTYL_DM_HISTORY_TOOL_REQUEST) { + role_label = "**Admin (Tool Request)**"; + } else if (entry->role == DIDACTYL_DM_HISTORY_TOOL_RESPONSE) { + role_label = "**You (Tool Response)**"; + } + + size_t need = strlen("- ") + strlen(role_label) + strlen(": ") + strlen(entry->content) + 2U; + if (used + need > cap) { + size_t next = cap; + while (used + need > next) next *= 2U; + char* grown = (char*)realloc(buf, next); + if (!grown) { + pthread_mutex_unlock(&g_dm_history_mutex); + free(buf); + return NULL; + } + buf = grown; + cap = next; + } + + snprintf(buf + used, cap - used, "- %s: %s\n", role_label, entry->content); + used += strlen(buf + used); + } + + pthread_mutex_unlock(&g_dm_history_mutex); + + if (used > 0 && buf[used - 1] == '\n') { + buf[used - 1] = '\0'; + } + + return buf; +} + char* nostr_handler_get_admin_kind0_context(void) { if (!g_cfg || !g_cfg->admin_context.enabled || !g_cfg->admin_context.track_kind_0) { return NULL; diff --git a/src/nostr_handler.h b/src/nostr_handler.h index 25faa2a..009c80b 100644 --- a/src/nostr_handler.h +++ b/src/nostr_handler.h @@ -102,6 +102,7 @@ int nostr_handler_dm_history_remember(const char* peer_pubkey_hex, const char* content, didactyl_dm_history_role_t role); char* nostr_handler_get_dm_history_json(const char* peer_pubkey_hex, int limit); +char* nostr_handler_get_dm_history_markdown(const char* peer_pubkey_hex, int limit); void nostr_handler_cleanup(void); #endif \ No newline at end of file diff --git a/src/prompt_template.c b/src/prompt_template.c index 2c94e56..8fdaf20 100644 --- a/src/prompt_template.c +++ b/src/prompt_template.c @@ -2,6 +2,7 @@ #include "prompt_template.h" #include "cjson/cJSON.h" +#include "json_to_markdown.h" #include #include @@ -177,9 +178,15 @@ char* prompt_template_resolve_inline_variables(const char* tpl, tools_context_t* if (!success || !cJSON_IsBool(success) || cJSON_IsTrue(success)) { cJSON* content = cJSON_GetObjectItemCaseSensitive(root, "content"); if (content && cJSON_IsString(content) && content->valuestring) { - replacement_owned = strdup(content->valuestring); + char* md = json_to_markdown(content->valuestring, 8); + replacement_owned = md ? md : strdup(content->valuestring); } else if (content) { - replacement_owned = cJSON_PrintUnformatted(content); + char* raw = cJSON_PrintUnformatted(content); + if (raw) { + char* md = json_to_markdown(raw, 8); + replacement_owned = md ? md : raw; + if (md) free(raw); + } } } } diff --git a/src/tools/tool_agent.c b/src/tools/tool_agent.c index fa1b6a6..e1a4d52 100644 --- a/src/tools/tool_agent.c +++ b/src/tools/tool_agent.c @@ -410,7 +410,7 @@ char* execute_nostr_dm_history(tools_context_t* ctx, const char* args_json) { continue; } - const char* prefix = (strcmp(role->valuestring, "user") == 0) ? "User: " : "Assistant: "; + const char* prefix = (strcmp(role->valuestring, "user") == 0) ? "- **Admin**: " : "- **You**: "; if (append_text_local(&rendered, &cap, &used, prefix) != 0 || append_text_local(&rendered, &cap, &used, content->valuestring) != 0 || append_text_local(&rendered, &cap, &used, "\n") != 0) { diff --git a/test_pool b/test_pool new file mode 100755 index 0000000..6f48606 Binary files /dev/null and b/test_pool differ diff --git a/test_pool.c b/test_pool.c new file mode 100644 index 0000000..abc3187 --- /dev/null +++ b/test_pool.c @@ -0,0 +1,267 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include + +#include "./nostr_core_lib/cjson/cJSON.h" +#include "./nostr_core_lib/nostr_core/nostr_core.h" + +static const char* k_default_relays[] = { + "wss://relay.damus.io", + "wss://relay.primal.net", + "wss://relay.laantungir.net" +}; + +typedef struct { + int eose_received; + int events_received; + int kind3_events; + int max_p_tags; +} test_ctx_t; + +static volatile int g_ws_eose_seen = 0; + +static long long now_ms(void) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + return ((long long)ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL); +} + +static int count_tag(cJSON* tags, const char* key) { + if (!tags || !cJSON_IsArray(tags) || !key) { + return 0; + } + + int n = cJSON_GetArraySize(tags); + int count = 0; + for (int i = 0; i < n; i++) { + cJSON* tag = cJSON_GetArrayItem(tags, i); + if (!tag || !cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 1) { + continue; + } + + cJSON* k = cJSON_GetArrayItem(tag, 0); + if (k && cJSON_IsString(k) && k->valuestring && strcmp(k->valuestring, key) == 0) { + count++; + } + } + return count; +} + +static void on_event(cJSON* event, const char* relay_url, void* user_data) { + test_ctx_t* ctx = (test_ctx_t*)user_data; + if (!ctx || !event || !cJSON_IsObject(event)) { + return; + } + + ctx->events_received++; + + cJSON* kind = cJSON_GetObjectItemCaseSensitive(event, "kind"); + cJSON* id = cJSON_GetObjectItemCaseSensitive(event, "id"); + cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(event, "pubkey"); + cJSON* created_at = cJSON_GetObjectItemCaseSensitive(event, "created_at"); + cJSON* content = cJSON_GetObjectItemCaseSensitive(event, "content"); + cJSON* tags = cJSON_GetObjectItemCaseSensitive(event, "tags"); + + int kind_val = (kind && cJSON_IsNumber(kind)) ? (int)kind->valuedouble : -1; + int p_tags = count_tag(tags, "p"); + int r_tags = count_tag(tags, "r"); + int tag_count = (tags && cJSON_IsArray(tags)) ? cJSON_GetArraySize(tags) : 0; + int content_len = (content && cJSON_IsString(content) && content->valuestring) ? (int)strlen(content->valuestring) : 0; + + if (p_tags > ctx->max_p_tags) { + ctx->max_p_tags = p_tags; + } + if (kind_val == 3) { + ctx->kind3_events++; + } + + printf("[EVENT] relay=%s kind=%d created_at=%lld tags=%d p_tags=%d r_tags=%d content_len=%d id=%.16s pubkey=%.16s\n", + relay_url ? relay_url : "", + kind_val, + (created_at && cJSON_IsNumber(created_at)) ? (long long)created_at->valuedouble : -1LL, + tag_count, + p_tags, + r_tags, + content_len, + (id && cJSON_IsString(id) && id->valuestring) ? id->valuestring : "", + (pubkey && cJSON_IsString(pubkey) && pubkey->valuestring) ? pubkey->valuestring : ""); +} + +static void on_eose(cJSON** events, int event_count, void* user_data) { + (void)events; + test_ctx_t* ctx = (test_ctx_t*)user_data; + if (!ctx) { + return; + } + + ctx->eose_received = 1; + printf("[EOSE] received for subscription (event_count=%d)\n", event_count); +} + +static void log_callback(int level, const char* component, const char* message, void* user_data) { + (void)user_data; + const char* lvl = "?"; + switch (level) { + case NOSTR_LOG_LEVEL_ERROR: lvl = "ERROR"; break; + case NOSTR_LOG_LEVEL_WARN: lvl = "WARN"; break; + case NOSTR_LOG_LEVEL_INFO: lvl = "INFO"; break; + case NOSTR_LOG_LEVEL_DEBUG: lvl = "DEBUG"; break; + case NOSTR_LOG_LEVEL_TRACE: lvl = "TRACE"; break; + default: break; + } + + if (!component) component = "core"; + if (!message) message = ""; + + if (strcmp(component, "websocket") == 0) { + printf("[NOSTR %s] [%s] %s\n", lvl, component, message); + if (strstr(message, "[\"EOSE\"") != NULL) { + g_ws_eose_seen = 1; + } + } +} + +int main(int argc, char** argv) { + const char* npub = "npub13lm5wf8dvsdnc2894pkhch9uf8phvw9varrv8zf4sc885hhdmc8q6lx7ks"; + int timeout_ms = 15000; + + if (argc >= 2 && argv[1] && argv[1][0] != '\0') { + npub = argv[1]; + } + if (argc >= 3) { + timeout_ms = atoi(argv[2]); + if (timeout_ms <= 0) { + timeout_ms = 15000; + } + } + + unsigned char pubkey[32] = {0}; + char pubkey_hex[65] = {0}; + + if (nostr_decode_npub(npub, pubkey) != 0) { + fprintf(stderr, "Failed to decode npub: %s\n", npub); + return 1; + } + nostr_bytes_to_hex(pubkey, 32, pubkey_hex); + + if (nostr_crypto_init() != 0) { + fprintf(stderr, "nostr_crypto_init failed\n"); + return 1; + } + + nostr_set_log_callback(log_callback, NULL); + nostr_set_log_level(NOSTR_LOG_LEVEL_TRACE); + + nostr_pool_reconnect_config_t* reconnect = nostr_pool_reconnect_config_default(); + if (!reconnect) { + fprintf(stderr, "nostr_pool_reconnect_config_default failed\n"); + nostr_crypto_cleanup(); + return 1; + } + + reconnect->enable_auto_reconnect = 1; + reconnect->ping_interval_seconds = 20; + reconnect->pong_timeout_seconds = 10; + + nostr_relay_pool_t* pool = nostr_relay_pool_create(reconnect); + if (!pool) { + fprintf(stderr, "nostr_relay_pool_create failed\n"); + nostr_crypto_cleanup(); + return 1; + } + + const int relay_count = (int)(sizeof(k_default_relays) / sizeof(k_default_relays[0])); + for (int i = 0; i < relay_count; i++) { + if (nostr_relay_pool_add_relay(pool, k_default_relays[i]) != 0) { + fprintf(stderr, "Failed to add relay: %s\n", k_default_relays[i]); + } + } + + cJSON* filter = cJSON_CreateObject(); + cJSON* kinds = cJSON_CreateArray(); + cJSON* authors = cJSON_CreateArray(); + if (!filter || !kinds || !authors) { + fprintf(stderr, "Failed to build filter JSON\n"); + cJSON_Delete(filter); + cJSON_Delete(kinds); + cJSON_Delete(authors); + nostr_relay_pool_destroy(pool); + nostr_crypto_cleanup(); + return 1; + } + + cJSON_AddItemToArray(kinds, cJSON_CreateNumber(3)); + cJSON_AddItemToObject(filter, "kinds", kinds); + cJSON_AddItemToArray(authors, cJSON_CreateString(pubkey_hex)); + cJSON_AddItemToObject(filter, "authors", authors); + cJSON_AddNumberToObject(filter, "limit", 16); + + test_ctx_t ctx; + memset(&ctx, 0, sizeof(ctx)); + + printf("Target npub: %s\n", npub); + printf("Target hex : %s\n", pubkey_hex); + printf("Relays (%d):\n", relay_count); + for (int i = 0; i < relay_count; i++) { + printf(" - %s\n", k_default_relays[i]); + } + printf("Subscribing to kind=3 with timeout=%d ms\n", timeout_ms); + + nostr_pool_subscription_t* sub = nostr_relay_pool_subscribe( + pool, + k_default_relays, + relay_count, + filter, + on_event, + on_eose, + &ctx, + 0, + 0, + NOSTR_POOL_EOSE_FIRST, + 12, + 20 + ); + + if (!sub) { + fprintf(stderr, "nostr_relay_pool_subscribe failed\n"); + cJSON_Delete(filter); + nostr_relay_pool_destroy(pool); + nostr_crypto_cleanup(); + return 1; + } + + long long started = now_ms(); + while (!ctx.eose_received && !g_ws_eose_seen) { + long long elapsed = now_ms() - started; + if (elapsed >= timeout_ms) { + break; + } + (void)nostr_relay_pool_poll(pool, 100); + } + + printf("\nSummary:\n"); + printf(" EOSE callback : %s\n", ctx.eose_received ? "yes" : "no"); + printf(" Websocket EOSE: %s\n", g_ws_eose_seen ? "yes" : "no"); + printf(" Events seen : %d\n", ctx.events_received); + printf(" Kind-3 events : %d\n", ctx.kind3_events); + printf(" Max p-tags : %d\n", ctx.max_p_tags); + + if (!ctx.eose_received && g_ws_eose_seen) { + fprintf(stderr, "NOTE: Relay sent EOSE on websocket but subscription callback did not fire (pool EOSE behavior mismatch).\n"); + } + if (!ctx.eose_received && !g_ws_eose_seen) { + fprintf(stderr, "Timed out waiting for EOSE after %d ms\n", timeout_ms); + } + + (void)nostr_pool_subscription_close(sub); + cJSON_Delete(filter); + nostr_relay_pool_destroy(pool); + nostr_crypto_cleanup(); + + return (ctx.eose_received || g_ws_eose_seen) ? 0 : 2; +} diff --git a/tests/configs/test_genesis.jsonc b/tests/configs/test_genesis.jsonc index 105941a..0215462 100644 --- a/tests/configs/test_genesis.jsonc +++ b/tests/configs/test_genesis.jsonc @@ -9,12 +9,12 @@ }, "dm_protocol": "nip04", "llm": { - "provider": "openai", - "api_key": "sk-REPLACE_WITH_API_KEY", + "provider": "https://api.ppq.ai", + "api_key": "sk-SBI2Pga0J6n8jpPorHbRHC", "model": "claude-haiku-4.5", - "base_url": "https://api.anthropic.com/v1", - "max_tokens": 512, - "temperature": 0.3 + "base_url": "https://api.ppq.ai", + "max_tokens": 10000, + "temperature": 0.7 }, "api": { "enabled": true, diff --git a/tests/harness/__pycache__/agent_process.cpython-313.pyc b/tests/harness/__pycache__/agent_process.cpython-313.pyc index 79284a8..6bcf5b8 100644 Binary files a/tests/harness/__pycache__/agent_process.cpython-313.pyc and b/tests/harness/__pycache__/agent_process.cpython-313.pyc differ diff --git a/tests/harness/agent_process.py b/tests/harness/agent_process.py index f0f5597..085b44f 100644 --- a/tests/harness/agent_process.py +++ b/tests/harness/agent_process.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import re import signal import subprocess import time @@ -54,6 +55,7 @@ class AgentProcess: commands = [ ["lsof", "-t", f"-iTCP:{self.api_port}", "-sTCP:LISTEN"], ["fuser", f"{self.api_port}/tcp"], + ["ss", "-ltnp"], ] for cmd in commands: @@ -61,7 +63,18 @@ class AgentProcess: proc = subprocess.run(cmd, capture_output=True, text=True, check=False) except FileNotFoundError: continue + output = f"{proc.stdout}\n{proc.stderr}" + + if cmd and cmd[0] == "ss": + port_token = f":{self.api_port}" + for line in output.splitlines(): + if port_token not in line: + continue + for m in re.finditer(r"pid=(\d+)", line): + pids.add(int(m.group(1))) + continue + for token in output.split(): if token.isdigit(): pids.add(int(token)) diff --git a/tests/results/20260404T135221Z/agent_debug.log b/tests/results/20260404T135221Z/agent_debug.log new file mode 100644 index 0000000..057363d --- /dev/null +++ b/tests/results/20260404T135221Z/agent_debug.log @@ -0,0 +1,390 @@ +[2026-04-04 10:04:51] [INFO ] [main.c:1016] Didactyl v0.2.20 starting +[2026-04-04 10:04:51] [INFO ] [nostr_handler.c:2207] [didactyl] initializing relay pool with 2 relays +[2026-04-04 10:04:51] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.damus.io +[2026-04-04 10:04:51] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.primal.net +[2026-04-04 10:04:51] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_1_1775311491", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_1_1775311491", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_1_1775311491"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_1_1775311491"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_1_1775311491"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_1_1775311491"] +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [01] Relay connectivity: begin +[2026-04-04 10:04:52] [INFO ] [main.c:255] [didactyl] startup checklist [01] Relay connectivity: ok (connected relays: 2/2) +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [02] Recover runtime config from Nostr: begin +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_2_1775311492", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_2_1775311492", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_2_1775311492"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_2_1775311492"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_2_1775311492"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_2_1775311492"] +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [02] Recover runtime config from Nostr: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [03] Validate LLM config: begin +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [03] Validate LLM config: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [04] Validate admin config: begin +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [04] Validate admin config: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [05] Initialize LLM client: begin +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [05] Initialize LLM client: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [06] Detect first run via kind 10002: begin +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_3_1775311492", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_3_1775311492", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_3_1775311492"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_3_1775311492"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_3_1775311492"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_3_1775311492"] +[2026-04-04 10:04:53] [INFO ] [main.c:1245] [didactyl] startup phase: first-run detection via kind 10002 => subsequent-run +[2026-04-04 10:04:53] [INFO ] [main.c:255] [didactyl] startup checklist [06] Detect first run via kind 10002: ok (subsequent-run) +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [07] Reconcile/persist startup state: begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_4_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_4_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=0 (profile) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10002 (relay_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=31124 (skill) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10123 (adoption_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3297] [didactyl] startup publish summary: 4/4 event(s) published to at least one relay; relays used=2/2 +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 10:04:53] [INFO ] [main.c:1267] [didactyl] startup phase: persisted encrypted runtime config to Nostr +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [07] Reconcile/persist startup state: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [08] Initialize agent: begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_5_1775311493", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_5_1775311493", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","98b892b33ce3731cbeb82d791f5e41c7deade4bb61756ed600a14f3aeaa7d45e",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","98b892b33ce3731cbeb82d791f5e41c7deade4bb61756ed600a14f3aeaa7d45e",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","7fb5e7e09092254b700df2e7a8473fdca42ee09e3a32b86d262ad2403807f920",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","7fb5e7e09092254b700df2e7a8473fdca42ee09e3a32b86d262ad2403807f920",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa",true,"duplicate: have this event"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","c27bd1317e1c716b8450c4302b42214397fd03d2620d39b8f28d3fe765b446d7",false,"rate-limited: you are noting too much"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_5_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","8842d5de102138e7f7bc72819624a5735360d575e1cd136cc69902b38544a717",false,"rate-limited: you are noting too much"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","8842d5de102138e7f7bc72819624a5735360d575e1cd136cc69902b38544a717",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_5_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","c27bd1317e1c716b8450c4302b42214397fd03d2620d39b8f28d3fe765b446d7",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_5_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_5_1775311493"] +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [08] Initialize agent: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [09] Initialize trigger manager: begin +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:814] [didactyl] trigger manager initialized (capacity=16) +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [09] Initialize trigger manager: ok +[2026-04-04 10:04:53] [INFO ] [main.c:1308] [didactyl] startup phase: deferred trigger load will run after self-skill EOSE +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [10] Load startup triggers: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1311] [didactyl] startup phase: startup-config trigger load begin +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1138] [didactyl] trigger added d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1021] [didactyl] startup trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1029] [didactyl] trigger manager loaded 1 trigger(s) from startup config (considered=1) +[2026-04-04 10:04:53] [INFO ] [main.c:1315] [didactyl] startup phase: startup-config trigger load end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [10] Load startup triggers: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [11] Discover self relay list via kind 10002: begin +[2026-04-04 10:04:53] [INFO ] [main.c:327] [didactyl] kind10002 query begin: timeout_ms=5000 author=1eb171136dbb1ba6... relay_count=2 +[2026-04-04 10:04:53] [INFO ] [main.c:334] [didactyl] kind10002 query relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":0,"events_published":6,"events_published_ok":4,"events_published_failed":2,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493,"last_publish_error":"rate-limited: you are noting too much"},{"url":"wss://relay.primal.net","status":"connected","events_received":3,"events_published":6,"events_published_ok":6,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493}]} +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_6_1775311493", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_6_1775311493", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_6_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_6_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_6_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_6_1775311493"] +[2026-04-04 10:04:53] [INFO ] [main.c:354] [didactyl] kind10002 query received event_count=1 +[2026-04-04 10:04:53] [INFO ] [main.c:451] [didactyl] kind10002 query extracted relays=["wss://relay.damus.io","wss://relay.primal.net"] +[2026-04-04 10:04:53] [INFO ] [main.c:556] [didactyl] kind10002 wait success: relay_count=2 +[2026-04-04 10:04:53] [INFO ] [main.c:558] [didactyl] kind10002 relay[0]=wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [main.c:558] [didactyl] kind10002 relay[1]=wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [main.c:562] [didactyl] kind10002 wait success relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":0,"events_published":6,"events_published_ok":4,"events_published_failed":2,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493,"last_publish_error":"rate-limited: you are noting too much"},{"url":"wss://relay.primal.net","status":"connected","events_received":4,"events_published":6,"events_published_ok":6,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493}]} +[2026-04-04 10:04:53] [INFO ] [main.c:255] [didactyl] startup checklist [11] Discover self relay list via kind 10002: ok (kind10002=2 added=0 connected=2/2) +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [12] Subscribe admin context: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1366] [didactyl] startup phase: subscribe admin context begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_7_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_7_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_8_1775311493", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_8_1775311493", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2352] [didactyl] admin context subscriptions active for admin 254d7a7f68b4443f... +[2026-04-04 10:04:53] [INFO ] [main.c:1370] [didactyl] startup phase: subscribe admin context end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [12] Subscribe admin context: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [13] Subscribe agent self context: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1374] [didactyl] startup phase: subscribe agent self context begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_9_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_9_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_10_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_10_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2440] [didactyl] agent self-context subscriptions active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:04:53] [INFO ] [main.c:1378] [didactyl] startup phase: subscribe agent self context end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [13] Subscribe agent self context: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [14] Subscribe self-skill cache: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1382] [didactyl] startup phase: subscribe self skill cache begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_11_1775311493", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_11_1775311493", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_12_1775311493", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_12_1775311493", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2532] [didactyl] self/admin skill subscriptions active (self=1eb171136dbb1ba6..., admin=254d7a7f68b4443f...) +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_7_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_7_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_8_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_8_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_9_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_9_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_10_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_10_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 via wss://relay.damus.io +[2026-04-04 10:04:54] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 via wss://relay.primal.net +[2026-04-04 10:04:54] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 via wss://relay.damus.io +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 via wss://relay.primal.net +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_11_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_11_1775311493"] +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:1725] [didactyl] self-skill EOSE received: cached skill events=0 +[2026-04-04 10:04:54] [INFO ] [main.c:44] [didactyl] deferred trigger load begin after self-skill EOSE (event_count=0) +[2026-04-04 10:04:54] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:54] [INFO ] [trigger_manager.c:918] [didactyl] trigger manager loaded 1 trigger(s) from self skills (considered=1) +[2026-04-04 10:04:54] [INFO ] [main.c:54] [didactyl] deferred trigger load status: {"success":true,"count":1,"active":1,"triggers":[{"skill_d_tag":"identity_and_rules","filter_json":"{\"from\":\"admin\"}","type":"dm","action":"llm","enabled":true,"subscribed":false,"llm":"","tools":"","has_max_tokens":false,"has_temperature":false,"has_seed":false,"last_fired":0,"last_seen_created_at":0}]} +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 +[2026-04-04 10:04:54] [INFO ] [main.c:1449] [didactyl] startup phase: subscribe self skill cache end +[2026-04-04 10:04:54] [INFO ] [main.c:255] [didactyl] startup checklist [14] Subscribe self-skill cache: ok (skills=1 adoptions=1) +[2026-04-04 10:04:54] [INFO ] [main.c:248] [didactyl] startup checklist [15] Subscribe DMs: begin +[2026-04-04 10:04:54] [INFO ] [main.c:1453] [didactyl] startup phase: subscribe DMs begin +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_13_1775311494", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 100 + }] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_13_1775311494", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 100 + }] +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2643] [didactyl] DM subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:2644] [didactyl] DEBUG DM subscription g_start_time=1775311491 now=1775311494 delta=3 relay_count=2 +[2026-04-04 10:04:54] [INFO ] [main.c:1468] [didactyl] startup phase: subscribe DMs end +[2026-04-04 10:04:54] [INFO ] [main.c:258] [didactyl] startup checklist [15] Subscribe DMs: ok +[2026-04-04 10:04:54] [INFO ] [main.c:248] [didactyl] startup checklist [16] Subscribe wallet events: begin +[2026-04-04 10:04:54] [INFO ] [main.c:1472] [didactyl] startup phase: subscribe wallet events begin +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_14_1775311494", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 500 + }] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_14_1775311494", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 500 + }] +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2696] [didactyl] wallet event subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:04:54] [INFO ] [main.c:1476] [didactyl] startup phase: subscribe wallet events end +[2026-04-04 10:04:54] [INFO ] [main.c:258] [didactyl] startup checklist [16] Subscribe wallet events: ok +[2026-04-04 10:04:54] [INFO ] [main.c:248] [didactyl] startup checklist [17] Initialize cashu wallet: begin +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_15_1775311494", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_15_1775311494", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_12_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_12_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_13_1775311494"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_13_1775311494"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_14_1775311494"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_14_1775311494"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_15_1775311494"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_15_1775311494"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_15_1775311494"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_15_1775311494"] +[2026-04-04 10:04:54] [WARN ] [main.c:1492] [didactyl] startup phase: cashu wallet load/create deferred to first tool call +[2026-04-04 10:04:54] [INFO ] [main.c:255] [didactyl] startup checklist [17] Initialize cashu wallet: ok (initialized; load/create deferred) +[2026-04-04 10:04:54] [WARN ] [main.c:1528] [didactyl] HTTP API disabled due to bind/init failure; agent will continue without local API +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1499] [didactyl] sending plaintext DM content: Didactyl Test Agent has started up and is online at 2026-04-04 10:04:54 (version v0.2.20, connected relays: 2/2). +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1491] [didactyl] sending encrypted DM event: {"pubkey":"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc","created_at":1775311494,"kind":4,"tags":[["p","254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"]],"content":"L7Q6tbtXVWYnJZFZkkVvVfG/+6NQa9V8UMMK2tICfBTaG9V2MfwaKg9cVmC5BkmDl44QuJo7HFMQWHhj9NRlM1D2mP8JVTFndXdVCM5KU6tslhRfrRpjRFhfkGd4+0/lpmhVSq7wUP3f7oLOAvQaH66mDA7bIebr5BIss/4rSFE=?iv=fOZr9Tl0qw3NBGqRXOJPAA==","id":"3edc6798888fa063a724c277ed553f35fcf339aa62a74b039edf35907bc4da9e","sig":"0fc1537a1729128950d0cdffee17822186b7fc1fc1b4c29cf1e23b8979ae7264b1e702600bfa6c9778916b316e2b8b1050a113c815ba8a890ad455b2d2947bdc"} +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:906] [didactyl] publish DM target relays (2): +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.damus.io (async) +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.primal.net (async) +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2852] [didactyl] sent DM 3edc6798888fa063... to 254d7a7f68b4443f... via 2 connected relay(s) +[2026-04-04 10:04:54] [INFO ] [main.c:255] [didactyl] startup checklist [18] READY: ok (agent online; entering main poll loop) +[2026-04-04 10:04:54] [INFO ] [main.c:1571] [didactyl] entering main poll loop +[2026-04-04 10:04:54] [INFO ] [main.c:1572] [didactyl] running with pubkey 1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","3edc6798888fa063a724c277ed553f35fcf339aa62a74b039edf35907bc4da9e",true,""] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","3edc6798888fa063a724c277ed553f35fcf339aa62a74b039edf35907bc4da9e",false,"rate-limited: you are noting too much"] +[2026-04-04 10:05:36] [INFO ] [main.c:1584] [didactyl] shutting down +[2026-04-04 10:05:36] [INFO ] [trigger_manager.c:1602] [didactyl] trigger manager cleaned up +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_7_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_7_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_8_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_8_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_9_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_9_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_10_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_10_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_11_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_11_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_12_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_12_1775311493"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_13_1775311494"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_13_1775311494"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_14_1775311494"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_14_1775311494"] diff --git a/tests/results/20260404T135221Z/runtime_test_genesis.jsonc b/tests/results/20260404T135221Z/runtime_test_genesis.jsonc new file mode 100644 index 0000000..8aa613f --- /dev/null +++ b/tests/results/20260404T135221Z/runtime_test_genesis.jsonc @@ -0,0 +1,53 @@ +{ + // TEST CONFIGURATION + // Use disposable keys/accounts only. + "key": { + "nsec": "d3011e59954403fb84729ab0e9fb239ba0f0238b3f353dfb5c08a32f4b3d21a1" + }, + "admin": { + "pubkey": "254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac" + }, + "dm_protocol": "nip04", + "llm": { + "provider": "https://api.ppq.ai", + "api_key": "sk-SBI2Pga0J6n8jpPorHbRHC", + "model": "claude-haiku-4.5", + "base_url": "https://api.ppq.ai", + "max_tokens": 10000, + "temperature": 0.7 + }, + "api": { + "enabled": true, + "port": 8485, + "bind_address": "127.0.0.1" + }, + "startup_events": [ + { + "kind": 0, + "content_fields": { + "name": "Didactyl Test Agent", + "about": "Automated test instance" + }, + "tags": [] + }, + { + "kind": 10002, + "content": "", + "tags": [ + ["r", "wss://relay.damus.io"], + ["r", "wss://relay.primal.net"] + ] + }, + { + "kind": 31124, + "content": "# Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed.", + "tags": [ + ["d", "identity_and_rules"], + ["app", "didactyl"], + ["scope", "private"], + ["trigger", "dm"], + ["filter", "{\"from\":\"admin\"}"] + ] + } + ] +} diff --git a/tests/results/20260404T135328Z/agent_debug.log b/tests/results/20260404T135328Z/agent_debug.log new file mode 100644 index 0000000..1ede16d --- /dev/null +++ b/tests/results/20260404T135328Z/agent_debug.log @@ -0,0 +1,574 @@ +[2026-04-04 09:53:31] [INFO ] [main.c:1016] Didactyl v0.2.20 starting +[2026-04-04 09:53:31] [INFO ] [nostr_handler.c:2207] [didactyl] initializing relay pool with 2 relays +[2026-04-04 09:53:31] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.damus.io +[2026-04-04 09:53:31] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.primal.net +[2026-04-04 09:53:31] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_1_1775310811", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 09:53:31] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_1_1775310811", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 09:53:31] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_1_1775310811"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_1_1775310811"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_1_1775310811"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_1_1775310811"] +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [01] Relay connectivity: begin +[2026-04-04 09:53:32] [INFO ] [main.c:255] [didactyl] startup checklist [01] Relay connectivity: ok (connected relays: 2/2) +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [02] Recover runtime config from Nostr: begin +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_2_1775310812", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_2_1775310812", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_2_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_2_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_2_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_2_1775310812"] +[2026-04-04 09:53:32] [INFO ] [main.c:258] [didactyl] startup checklist [02] Recover runtime config from Nostr: ok +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [03] Validate LLM config: begin +[2026-04-04 09:53:32] [INFO ] [main.c:258] [didactyl] startup checklist [03] Validate LLM config: ok +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [04] Validate admin config: begin +[2026-04-04 09:53:32] [INFO ] [main.c:258] [didactyl] startup checklist [04] Validate admin config: ok +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [05] Initialize LLM client: begin +[2026-04-04 09:53:32] [INFO ] [main.c:258] [didactyl] startup checklist [05] Initialize LLM client: ok +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [06] Detect first run via kind 10002: begin +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_3_1775310812", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_3_1775310812", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_3_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_3_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_3_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_3_1775310812"] +[2026-04-04 09:53:32] [INFO ] [main.c:1245] [didactyl] startup phase: first-run detection via kind 10002 => subsequent-run +[2026-04-04 09:53:32] [INFO ] [main.c:255] [didactyl] startup checklist [06] Detect first run via kind 10002: ok (subsequent-run) +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [07] Reconcile/persist startup state: begin +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_4_1775310812", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_4_1775310812", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_4_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_4_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_4_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_4_1775310812"] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=0 (profile) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10002 (relay_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=31124 (skill) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10123 (adoption_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:3297] [didactyl] startup publish summary: 4/4 event(s) published to at least one relay; relays used=2/2 +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 09:53:32] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 09:53:32] [INFO ] [main.c:1267] [didactyl] startup phase: persisted encrypted runtime config to Nostr +[2026-04-04 09:53:32] [INFO ] [main.c:258] [didactyl] startup checklist [07] Reconcile/persist startup state: ok +[2026-04-04 09:53:32] [INFO ] [main.c:248] [didactyl] startup checklist [08] Initialize agent: begin +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_5_1775310812", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 09:53:32] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_5_1775310812", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","62db0aa31438a53f9811b70aa5de7d8b5e07f9327a1df1a1f28d1c56f2041cdd",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","62db0aa31438a53f9811b70aa5de7d8b5e07f9327a1df1a1f28d1c56f2041cdd",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","b2c46547046ec744abd08f147d49c1d218011fef013e6effc94fde7203db60d8",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","b2c46547046ec744abd08f147d49c1d218011fef013e6effc94fde7203db60d8",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","8e6842039e11bfabaa9ba7e2c9e94c9a75a87b2b2a8ea3f8c9ea4a31a8bbbbf8",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","8e6842039e11bfabaa9ba7e2c9e94c9a75a87b2b2a8ea3f8c9ea4a31a8bbbbf8",false,"rate-limited: you are noting too much"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","912179848fa53e52e842e13b124c8e49d3015ef48b15a079ea4bd687b6251fc4",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","912179848fa53e52e842e13b124c8e49d3015ef48b15a079ea4bd687b6251fc4",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_5_1775310812"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_5_1775310812"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","07e867604bb4b8bd8a8925cdc485f437c7e6463a3cf039d3f3b1af42f4f6e95e",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_5_1775310812"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_5_1775310812"] +[2026-04-04 09:53:33] [INFO ] [main.c:258] [didactyl] startup checklist [08] Initialize agent: ok +[2026-04-04 09:53:33] [INFO ] [main.c:248] [didactyl] startup checklist [09] Initialize trigger manager: begin +[2026-04-04 09:53:33] [INFO ] [trigger_manager.c:814] [didactyl] trigger manager initialized (capacity=16) +[2026-04-04 09:53:33] [INFO ] [main.c:258] [didactyl] startup checklist [09] Initialize trigger manager: ok +[2026-04-04 09:53:33] [INFO ] [main.c:1308] [didactyl] startup phase: deferred trigger load will run after self-skill EOSE +[2026-04-04 09:53:33] [INFO ] [main.c:248] [didactyl] startup checklist [10] Load startup triggers: begin +[2026-04-04 09:53:33] [INFO ] [main.c:1311] [didactyl] startup phase: startup-config trigger load begin +[2026-04-04 09:53:33] [INFO ] [trigger_manager.c:1138] [didactyl] trigger added d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:53:33] [INFO ] [trigger_manager.c:1021] [didactyl] startup trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:53:33] [INFO ] [trigger_manager.c:1029] [didactyl] trigger manager loaded 1 trigger(s) from startup config (considered=1) +[2026-04-04 09:53:33] [INFO ] [main.c:1315] [didactyl] startup phase: startup-config trigger load end +[2026-04-04 09:53:33] [INFO ] [main.c:258] [didactyl] startup checklist [10] Load startup triggers: ok +[2026-04-04 09:53:33] [INFO ] [main.c:248] [didactyl] startup checklist [11] Discover self relay list via kind 10002: begin +[2026-04-04 09:53:33] [INFO ] [main.c:327] [didactyl] kind10002 query begin: timeout_ms=5000 author=1eb171136dbb1ba6... relay_count=2 +[2026-04-04 09:53:33] [INFO ] [main.c:334] [didactyl] kind10002 query relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":0,"events_published":6,"events_published_ok":3,"events_published_failed":1,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775310811,"last_event_time":1775310813,"last_publish_error":"rate-limited: you are noting too much"},{"url":"wss://relay.primal.net","status":"connected","events_received":2,"events_published":6,"events_published_ok":5,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775310811,"last_event_time":1775310813}]} +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_6_1775310813", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_6_1775310813", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","b4ed5a24cfff8247076e5d3320511fee0a0546ec0000974382422e8f82e8ecb7",false,"rate-limited: you are noting too much"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","b4ed5a24cfff8247076e5d3320511fee0a0546ec0000974382422e8f82e8ecb7",true,""] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","07e867604bb4b8bd8a8925cdc485f437c7e6463a3cf039d3f3b1af42f4f6e95e",false,"rate-limited: you are noting too much"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_6_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_6_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_6_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_6_1775310813"] +[2026-04-04 09:53:33] [INFO ] [main.c:354] [didactyl] kind10002 query received event_count=1 +[2026-04-04 09:53:33] [INFO ] [main.c:451] [didactyl] kind10002 query extracted relays=["wss://relay.damus.io","wss://relay.primal.net"] +[2026-04-04 09:53:33] [INFO ] [main.c:556] [didactyl] kind10002 wait success: relay_count=2 +[2026-04-04 09:53:33] [INFO ] [main.c:558] [didactyl] kind10002 relay[0]=wss://relay.damus.io +[2026-04-04 09:53:33] [INFO ] [main.c:558] [didactyl] kind10002 relay[1]=wss://relay.primal.net +[2026-04-04 09:53:33] [INFO ] [main.c:562] [didactyl] kind10002 wait success relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":1,"events_published":6,"events_published_ok":3,"events_published_failed":3,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775310811,"last_event_time":1775310813,"last_publish_error":"rate-limited: you are noting too much"},{"url":"wss://relay.primal.net","status":"connected","events_received":2,"events_published":6,"events_published_ok":6,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775310811,"last_event_time":1775310813}]} +[2026-04-04 09:53:33] [INFO ] [main.c:255] [didactyl] startup checklist [11] Discover self relay list via kind 10002: ok (kind10002=2 added=0 connected=2/2) +[2026-04-04 09:53:33] [INFO ] [main.c:248] [didactyl] startup checklist [12] Subscribe admin context: begin +[2026-04-04 09:53:33] [INFO ] [main.c:1366] [didactyl] startup phase: subscribe admin context begin +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_7_1775310813", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_7_1775310813", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_8_1775310813", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_8_1775310813", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 09:53:33] [INFO ] [nostr_handler.c:2352] [didactyl] admin context subscriptions active for admin 254d7a7f68b4443f... +[2026-04-04 09:53:33] [INFO ] [main.c:1370] [didactyl] startup phase: subscribe admin context end +[2026-04-04 09:53:33] [INFO ] [main.c:258] [didactyl] startup checklist [12] Subscribe admin context: ok +[2026-04-04 09:53:33] [INFO ] [main.c:248] [didactyl] startup checklist [13] Subscribe agent self context: begin +[2026-04-04 09:53:33] [INFO ] [main.c:1374] [didactyl] startup phase: subscribe agent self context begin +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_9_1775310813", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_9_1775310813", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_10_1775310813", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_10_1775310813", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 09:53:33] [INFO ] [nostr_handler.c:2440] [didactyl] agent self-context subscriptions active for pubkey 1eb171136dbb1ba6... +[2026-04-04 09:53:33] [INFO ] [main.c:1378] [didactyl] startup phase: subscribe agent self context end +[2026-04-04 09:53:33] [INFO ] [main.c:258] [didactyl] startup checklist [13] Subscribe agent self context: ok +[2026-04-04 09:53:33] [INFO ] [main.c:248] [didactyl] startup checklist [14] Subscribe self-skill cache: begin +[2026-04-04 09:53:33] [INFO ] [main.c:1382] [didactyl] startup phase: subscribe self skill cache begin +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_11_1775310813", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_11_1775310813", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_12_1775310813", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_12_1775310813", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 09:53:33] [INFO ] [nostr_handler.c:2532] [didactyl] self/admin skill subscriptions active (self=1eb171136dbb1ba6..., admin=254d7a7f68b4443f...) +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_7_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_8_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_9_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_10_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:33] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=9f094c21eade42f8c2f23fbce8127d13e6f0baacef8ead7afca66dd54d2033a7 created_at=1775310813 via wss://relay.primal.net +[2026-04-04 09:53:33] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:53:33] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:33] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=62107bfc20c3b449541878519bed40ebf94c21d49a1a9bb1da55c3f2d035fa7d created_at=1775310813 via wss://relay.primal.net +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_11_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_7_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_12_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_8_1775310813"] +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 09:53:33] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_9_1775310813"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_10_1775310813"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=912179848fa53e52e842e13b124c8e49d3015ef48b15a079ea4bd687b6251fc4 created_at=1775310812 via wss://relay.damus.io +[2026-04-04 09:53:34] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=e54b9c4384ee5e214a337f153c9cb7ecc61b46b77bce99851c2b80166cc3dd6d created_at=1775310771 via wss://relay.damus.io +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_11_1775310813"] +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:1725] [didactyl] self-skill EOSE received: cached skill events=0 +[2026-04-04 09:53:34] [INFO ] [main.c:44] [didactyl] deferred trigger load begin after self-skill EOSE (event_count=0) +[2026-04-04 09:53:34] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:53:34] [INFO ] [trigger_manager.c:918] [didactyl] trigger manager loaded 1 trigger(s) from self skills (considered=1) +[2026-04-04 09:53:34] [INFO ] [main.c:54] [didactyl] deferred trigger load status: {"success":true,"count":1,"active":1,"triggers":[{"skill_d_tag":"identity_and_rules","filter_json":"{\"from\":\"admin\"}","type":"dm","action":"llm","enabled":true,"subscribed":false,"llm":"","tools":"","has_max_tokens":false,"has_temperature":false,"has_seed":false,"last_fired":0,"last_seen_created_at":0}]} +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=31124 d_tag=identity_and_rules id=9f094c21eade42f8c2f23fbce8127d13e6f0baacef8ead7afca66dd54d2033a7 created_at=1775310813 +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=10123 d_tag=- id=62107bfc20c3b449541878519bed40ebf94c21d49a1a9bb1da55c3f2d035fa7d created_at=1775310813 +[2026-04-04 09:53:34] [INFO ] [main.c:1449] [didactyl] startup phase: subscribe self skill cache end +[2026-04-04 09:53:34] [INFO ] [main.c:255] [didactyl] startup checklist [14] Subscribe self-skill cache: ok (skills=1 adoptions=1) +[2026-04-04 09:53:34] [INFO ] [main.c:248] [didactyl] startup checklist [15] Subscribe DMs: begin +[2026-04-04 09:53:34] [INFO ] [main.c:1453] [didactyl] startup phase: subscribe DMs begin +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_13_1775310814", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775310811, + "limit": 100 + }] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_13_1775310814", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775310811, + "limit": 100 + }] +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2643] [didactyl] DM subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:2644] [didactyl] DEBUG DM subscription g_start_time=1775310811 now=1775310814 delta=3 relay_count=2 +[2026-04-04 09:53:34] [INFO ] [main.c:1468] [didactyl] startup phase: subscribe DMs end +[2026-04-04 09:53:34] [INFO ] [main.c:258] [didactyl] startup checklist [15] Subscribe DMs: ok +[2026-04-04 09:53:34] [INFO ] [main.c:248] [didactyl] startup checklist [16] Subscribe wallet events: begin +[2026-04-04 09:53:34] [INFO ] [main.c:1472] [didactyl] startup phase: subscribe wallet events begin +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_14_1775310814", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775310811, + "limit": 500 + }] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_14_1775310814", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775310811, + "limit": 500 + }] +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2696] [didactyl] wallet event subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 09:53:34] [INFO ] [main.c:1476] [didactyl] startup phase: subscribe wallet events end +[2026-04-04 09:53:34] [INFO ] [main.c:258] [didactyl] startup checklist [16] Subscribe wallet events: ok +[2026-04-04 09:53:34] [INFO ] [main.c:248] [didactyl] startup checklist [17] Initialize cashu wallet: begin +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_15_1775310814", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_15_1775310814", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_12_1775310813"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_13_1775310814"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_14_1775310814"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_13_1775310814"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_15_1775310814"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_14_1775310814"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_15_1775310814"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_15_1775310814"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_15_1775310814"] +[2026-04-04 09:53:34] [WARN ] [main.c:1492] [didactyl] startup phase: cashu wallet load/create deferred to first tool call +[2026-04-04 09:53:34] [INFO ] [main.c:255] [didactyl] startup checklist [17] Initialize cashu wallet: ok (initialized; load/create deferred) +[2026-04-04 09:53:34] [WARN ] [main.c:1528] [didactyl] HTTP API disabled due to bind/init failure; agent will continue without local API +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:1499] [didactyl] sending plaintext DM content: Didactyl Test Agent has started up and is online at 2026-04-04 09:53:34 (version v0.2.20, connected relays: 2/2). +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:1491] [didactyl] sending encrypted DM event: {"pubkey":"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc","created_at":1775310814,"kind":4,"tags":[["p","254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"]],"content":"2IdS5q7P2xG6npiwo8XNCheTJ0ORjlriZhvvgByCrOrcY7U4roaoXA0AIJXM9CQxWLSkrWaXi0h1DTSOPZVCo5SqhQHEW4phQRWSz/bKnTjd3v8FTbCqO/JG1ghELrtmc3wED7CkEudSYgIEn46YYiYtomq/ulvkx7W6cm38tRM=?iv=jGIH5/frLMaWsdBfOKb0Gw==","id":"67ea8af90c4f8e8f1d9267beea62ce3c85c38493e386d22019b8a51b430782ba","sig":"1d6589f39630ad5e278b6c2a4b550903f7f09a0e5f5c4a42257a36e718c10ae9d959db9c73d72a3ab597baeac41076a71c7dfc3a87e6ba234e98946a6205dbef"} +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:906] [didactyl] publish DM target relays (2): +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.damus.io (async) +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.primal.net (async) +[2026-04-04 09:53:34] [INFO ] [nostr_handler.c:2852] [didactyl] sent DM 67ea8af90c4f8e8f... to 254d7a7f68b4443f... via 2 connected relay(s) +[2026-04-04 09:53:34] [INFO ] [main.c:255] [didactyl] startup checklist [18] READY: ok (agent online; entering main poll loop) +[2026-04-04 09:53:34] [INFO ] [main.c:1571] [didactyl] entering main poll loop +[2026-04-04 09:53:34] [INFO ] [main.c:1572] [didactyl] running with pubkey 1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","67ea8af90c4f8e8f1d9267beea62ce3c85c38493e386d22019b8a51b430782ba",false,"rate-limited: you are noting too much"] +[2026-04-04 09:53:34] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","67ea8af90c4f8e8f1d9267beea62ce3c85c38493e386d22019b8a51b430782ba",true,""] +[2026-04-04 09:53:37] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:37] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:37] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:37] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=9153008526c935129f5e6aad28d37b8cf73eade2cac7a581ea19bb31ddfb77fd created_at=1775310817 via wss://relay.primal.net +[2026-04-04 09:53:37] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:53:37] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:53:37] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:37] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=e3c57af82d9726d819e56625794a1887914536576cc80c6dd9b5795dc533699b created_at=1775310817 via wss://relay.primal.net +[2026-04-04 09:53:38] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:42] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=6136e4b953dff6c275673c984419e3732528d588de5eedd8d492123638d4a459 created_at=1775310821 via wss://relay.primal.net +[2026-04-04 09:53:42] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:53:42] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:53:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:42] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=a42e4d656c81b6393d9ca684b2c4609d1268e8b41e6a6170ef33c8d25d448b8d created_at=1775310821 via wss://relay.primal.net +[2026-04-04 09:53:43] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:46] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:46] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:46] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:46] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=3e46947f0f9953c994de18c03873c8290528791d5e161489d3830288e758851e created_at=1775310826 via wss://relay.primal.net +[2026-04-04 09:53:46] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:53:46] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:53:46] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:53:46] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=555277e0d773c2c05dfd59495698e0f2ae63b88a2c892716d34fbf731f90c93b created_at=1775310826 via wss://relay.primal.net +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=199821474ffaaedf3e7d90e8848fb95adfb3c547d25e62292f2ae01c6d15be26 created_at=1775310905 via wss://relay.damus.io +[2026-04-04 09:55:05] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:55:05] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=199821474ffaaedf3e7d90e8848fb95adfb3c547d25e62292f2ae01c6d15be26 created_at=1775310905 via wss://relay.primal.net +[2026-04-04 09:55:05] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:55:05] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=34ab5236e0848a5d27a6d98be6c3962919867267d861ae88d7887c186dc59d6f created_at=1775310905 via wss://relay.damus.io +[2026-04-04 09:55:05] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:55:05] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=34ab5236e0848a5d27a6d98be6c3962919867267d861ae88d7887c186dc59d6f created_at=1775310905 via wss://relay.primal.net +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=3593e19374c0110c7fc6d62dddb4d5ea5aa96d71a6b5a87df3f58e7e6044b721 created_at=1775310986 via wss://relay.damus.io +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=3593e19374c0110c7fc6d62dddb4d5ea5aa96d71a6b5a87df3f58e7e6044b721 created_at=1775310986 via wss://relay.primal.net +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=a099e3e10e33e4874c687dd7a6c19efc4cf96500c9190279e8f7a0a02ba655bb created_at=1775310986 via wss://relay.damus.io +[2026-04-04 09:56:27] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:56:27] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:56:27] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:56:27] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=a099e3e10e33e4874c687dd7a6c19efc4cf96500c9190279e8f7a0a02ba655bb created_at=1775310986 via wss://relay.primal.net +[2026-04-04 09:56:27] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:56:27] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=6b1239e90ef1eb0cc6d01d5e23e46a35075f8676fa9bdee1f27844da3bceebae created_at=1775311069 via wss://relay.damus.io +[2026-04-04 09:57:50] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:57:50] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=6b1239e90ef1eb0cc6d01d5e23e46a35075f8676fa9bdee1f27844da3bceebae created_at=1775311069 via wss://relay.primal.net +[2026-04-04 09:57:50] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:57:50] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=93a70c1d405d848e4c0d8e6b73b1e5ca9111c06549ddce551a275590846865ab created_at=1775311069 via wss://relay.damus.io +[2026-04-04 09:57:50] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:57:50] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=93a70c1d405d848e4c0d8e6b73b1e5ca9111c06549ddce551a275590846865ab created_at=1775311069 via wss://relay.primal.net +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=20ee7cfd49445a14a8163d37cc2113989f08db64d5bd97b6635e7b71d5067397 created_at=1775311149 via wss://relay.damus.io +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=20ee7cfd49445a14a8163d37cc2113989f08db64d5bd97b6635e7b71d5067397 created_at=1775311149 via wss://relay.primal.net +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=a1da473de1325a5a6d0b64d7f4435ac61ae8ebde1a7a76a3abb27746529e0414 created_at=1775311149 via wss://relay.damus.io +[2026-04-04 09:59:10] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:59:10] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 09:59:10] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 09:59:10] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=a1da473de1325a5a6d0b64d7f4435ac61ae8ebde1a7a76a3abb27746529e0414 created_at=1775311149 via wss://relay.primal.net +[2026-04-04 09:59:10] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 09:59:10] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:00:31] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=23e119df15e5dab7da6052bef17b973eb732d64f96f0cb81bae55c7eafe0cf84 created_at=1775311231 via wss://relay.damus.io +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:00:31] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=23e119df15e5dab7da6052bef17b973eb732d64f96f0cb81bae55c7eafe0cf84 created_at=1775311231 via wss://relay.primal.net +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:00:31] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=af0e63a9aa9b8dcdec1705ef2b0e6eb183a760563916ac1abe4ccd4b3ff31b9e created_at=1775311231 via wss://relay.damus.io +[2026-04-04 10:00:31] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:00:31] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:00:31] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=af0e63a9aa9b8dcdec1705ef2b0e6eb183a760563916ac1abe4ccd4b3ff31b9e created_at=1775311231 via wss://relay.primal.net +[2026-04-04 10:00:31] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:00:31] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:00:31] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:54] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=b08288f7f0691347979d73d8224d59ab518b4c4903d467261c6f54a4ac6438a6 created_at=1775311314 via wss://relay.damus.io +[2026-04-04 10:01:55] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:55] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=e2da3b48d758a294257443571dbfaede7befff218e0a7ed633f8ae8d90d44b11 created_at=1775311314 via wss://relay.damus.io +[2026-04-04 10:01:55] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:01:55] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:01:55] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:55] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=b08288f7f0691347979d73d8224d59ab518b4c4903d467261c6f54a4ac6438a6 created_at=1775311314 via wss://relay.primal.net +[2026-04-04 10:01:55] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:01:55] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=e2da3b48d758a294257443571dbfaede7befff218e0a7ed633f8ae8d90d44b11 created_at=1775311314 via wss://relay.primal.net +[2026-04-04 10:01:55] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:01:55] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b1e317e969767bd23746d85d16932a907d45c49911ec2d954f45253a0afc8c45 created_at=1775311361 via wss://relay.damus.io +[2026-04-04 10:02:42] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=4ce55cdf481bdc52128adcf546c526819e910e6e429510a67b16aee6ab8a27ce created_at=1775311361 via wss://relay.primal.net +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b1e317e969767bd23746d85d16932a907d45c49911ec2d954f45253a0afc8c45 created_at=1775311361 via wss://relay.primal.net +[2026-04-04 10:02:42] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=1de0c656f13e494782235710020ec1863236f385ea9bbde63973e32fac22e96e created_at=1775311362 via wss://relay.primal.net +[2026-04-04 10:02:42] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=5e118f362ee720887516af8389a895327771a32d97be51d179061cc959856306 created_at=1775311362 via wss://relay.primal.net +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:42] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:02:43] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=91a98af6794b4cda3e23918a72596ea072432a3bf61d0d2f175266555f69ad2c created_at=1775311427 via wss://relay.damus.io +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=91a98af6794b4cda3e23918a72596ea072432a3bf61d0d2f175266555f69ad2c created_at=1775311427 via wss://relay.primal.net +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=a101386a4d04cfe90e3278a442f7d8a5666b822551679be9e1da7ee3b50e66c0 created_at=1775311427 via wss://relay.damus.io +[2026-04-04 10:03:47] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:03:47] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:03:47] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:47] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=a101386a4d04cfe90e3278a442f7d8a5666b822551679be9e1da7ee3b50e66c0 created_at=1775311427 via wss://relay.primal.net +[2026-04-04 10:03:47] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:03:47] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:03:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:03:54] [TRACE] [nostr_handler.c:1505] [didactyl] DEBUG on_event ENTRY from wss://relay.primal.net (event=0x75dedd26ac00 g_cfg=0x7fff11e75dd0 g_dm_callback=set) +[2026-04-04 10:03:54] [TRACE] [nostr_handler.c:1539] [didactyl] DEBUG on_event: kind=4 id=d11595fbb84cc4e0... from=1eb171136dbb1ba6... via wss://relay.primal.net +[2026-04-04 10:03:54] [TRACE] [nostr_handler.c:1491] [didactyl] received encrypted DM event: {"content":"vU6HDxbHyDPXuQc6QBb6mKaxpT/v84wpTSRnDxIKB9HUtoMXbkmSmEWOE84JW8dm?iv=COBkis+eKnjifb3Qs2dSCg==","created_at":1775311434,"id":"d11595fbb84cc4e0d86c0791c1fc2fb0cd3c00f0556fa80529a6b2004b4d8eee","kind":4,"pubkey":"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc","sig":"7db392af3ada4cba8fb8d864629f503e864797cb7d76577e573ece9127e828bd82cc4b6366a2ed97dec524be412a32b9389f46f678c72dcaac6f66d3f1e206d3","tags":[["p","1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"]]} +[2026-04-04 10:03:54] [TRACE] [nostr_handler.c:1499] [didactyl] received decrypted DM content: This is a test DM from me to myself! +[2026-04-04 10:03:54] [TRACE] [nostr_handler.c:1664] [didactyl] DEBUG on_event: ignoring self-sent DM 1eb171136dbb1ba6... via wss://relay.primal.net +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 via wss://relay.damus.io +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 via wss://relay.primal.net +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 via wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 via wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:05:36] [INFO ] [main.c:1584] [didactyl] shutting down +[2026-04-04 10:05:36] [INFO ] [trigger_manager.c:1602] [didactyl] trigger manager cleaned up +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_7_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_7_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_8_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_8_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_9_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_9_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_10_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_10_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_11_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_11_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_12_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_12_1775310813"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_13_1775310814"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_13_1775310814"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_14_1775310814"] +[2026-04-04 10:05:36] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_14_1775310814"] diff --git a/tests/results/20260404T135328Z/runtime_test_genesis.jsonc b/tests/results/20260404T135328Z/runtime_test_genesis.jsonc new file mode 100644 index 0000000..8aa613f --- /dev/null +++ b/tests/results/20260404T135328Z/runtime_test_genesis.jsonc @@ -0,0 +1,53 @@ +{ + // TEST CONFIGURATION + // Use disposable keys/accounts only. + "key": { + "nsec": "d3011e59954403fb84729ab0e9fb239ba0f0238b3f353dfb5c08a32f4b3d21a1" + }, + "admin": { + "pubkey": "254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac" + }, + "dm_protocol": "nip04", + "llm": { + "provider": "https://api.ppq.ai", + "api_key": "sk-SBI2Pga0J6n8jpPorHbRHC", + "model": "claude-haiku-4.5", + "base_url": "https://api.ppq.ai", + "max_tokens": 10000, + "temperature": 0.7 + }, + "api": { + "enabled": true, + "port": 8485, + "bind_address": "127.0.0.1" + }, + "startup_events": [ + { + "kind": 0, + "content_fields": { + "name": "Didactyl Test Agent", + "about": "Automated test instance" + }, + "tags": [] + }, + { + "kind": 10002, + "content": "", + "tags": [ + ["r", "wss://relay.damus.io"], + ["r", "wss://relay.primal.net"] + ] + }, + { + "kind": 31124, + "content": "# Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed.", + "tags": [ + ["d", "identity_and_rules"], + ["app", "didactyl"], + ["scope", "private"], + ["trigger", "dm"], + ["filter", "{\"from\":\"admin\"}"] + ] + } + ] +} diff --git a/tests/results/20260404T140236Z/agent_debug.log b/tests/results/20260404T140236Z/agent_debug.log new file mode 100644 index 0000000..9b3aa70 --- /dev/null +++ b/tests/results/20260404T140236Z/agent_debug.log @@ -0,0 +1,395 @@ +[2026-04-04 10:04:51] [INFO ] [main.c:1016] Didactyl v0.2.20 starting +[2026-04-04 10:04:51] [INFO ] [nostr_handler.c:2207] [didactyl] initializing relay pool with 2 relays +[2026-04-04 10:04:51] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.damus.io +[2026-04-04 10:04:51] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.primal.net +[2026-04-04 10:04:51] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_1_1775311491", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_1_1775311491", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_1_1775311491"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_1_1775311491"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_1_1775311491"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_1_1775311491"] +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [01] Relay connectivity: begin +[2026-04-04 10:04:52] [INFO ] [main.c:255] [didactyl] startup checklist [01] Relay connectivity: ok (connected relays: 2/2) +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [02] Recover runtime config from Nostr: begin +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_2_1775311492", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_2_1775311492", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_2_1775311492"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_2_1775311492"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_2_1775311492"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_2_1775311492"] +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [02] Recover runtime config from Nostr: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [03] Validate LLM config: begin +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [03] Validate LLM config: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [04] Validate admin config: begin +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [04] Validate admin config: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [05] Initialize LLM client: begin +[2026-04-04 10:04:52] [INFO ] [main.c:258] [didactyl] startup checklist [05] Initialize LLM client: ok +[2026-04-04 10:04:52] [INFO ] [main.c:248] [didactyl] startup checklist [06] Detect first run via kind 10002: begin +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_3_1775311492", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_3_1775311492", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_3_1775311492"] +[2026-04-04 10:04:52] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_3_1775311492"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_3_1775311492"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_3_1775311492"] +[2026-04-04 10:04:53] [INFO ] [main.c:1245] [didactyl] startup phase: first-run detection via kind 10002 => subsequent-run +[2026-04-04 10:04:53] [INFO ] [main.c:255] [didactyl] startup checklist [06] Detect first run via kind 10002: ok (subsequent-run) +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [07] Reconcile/persist startup state: begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_4_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_4_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_4_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=0 (profile) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10002 (relay_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=31124 (skill) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10123 (adoption_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3297] [didactyl] startup publish summary: 4/4 event(s) published to at least one relay; relays used=2/2 +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 10:04:53] [INFO ] [main.c:1267] [didactyl] startup phase: persisted encrypted runtime config to Nostr +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [07] Reconcile/persist startup state: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [08] Initialize agent: begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_5_1775311493", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_5_1775311493", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","7fb5e7e09092254b700df2e7a8473fdca42ee09e3a32b86d262ad2403807f920",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","7fb5e7e09092254b700df2e7a8473fdca42ee09e3a32b86d262ad2403807f920",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","98b892b33ce3731cbeb82d791f5e41c7deade4bb61756ed600a14f3aeaa7d45e",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","98b892b33ce3731cbeb82d791f5e41c7deade4bb61756ed600a14f3aeaa7d45e",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_5_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_5_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_5_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_5_1775311493"] +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [08] Initialize agent: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [09] Initialize trigger manager: begin +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:814] [didactyl] trigger manager initialized (capacity=16) +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [09] Initialize trigger manager: ok +[2026-04-04 10:04:53] [INFO ] [main.c:1308] [didactyl] startup phase: deferred trigger load will run after self-skill EOSE +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [10] Load startup triggers: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1311] [didactyl] startup phase: startup-config trigger load begin +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1138] [didactyl] trigger added d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1021] [didactyl] startup trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1029] [didactyl] trigger manager loaded 1 trigger(s) from startup config (considered=1) +[2026-04-04 10:04:53] [INFO ] [main.c:1315] [didactyl] startup phase: startup-config trigger load end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [10] Load startup triggers: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [11] Discover self relay list via kind 10002: begin +[2026-04-04 10:04:53] [INFO ] [main.c:327] [didactyl] kind10002 query begin: timeout_ms=5000 author=1eb171136dbb1ba6... relay_count=2 +[2026-04-04 10:04:53] [INFO ] [main.c:334] [didactyl] kind10002 query relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":0,"events_published":6,"events_published_ok":4,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493},{"url":"wss://relay.primal.net","status":"connected","events_received":3,"events_published":6,"events_published_ok":4,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493}]} +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_6_1775311493", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_6_1775311493", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","e94ea27dc06a8f4f1e70e7e96d1d3e7f328016ff1bbbc3a50723268f14277fb1",false,"rate-limited: you are noting too much"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","cfe5aafd9e84b8a7c6caf1842ce51a502a156b5706ecddadaee73b106796bc6c",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","cfe5aafd9e84b8a7c6caf1842ce51a502a156b5706ecddadaee73b106796bc6c",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","e94ea27dc06a8f4f1e70e7e96d1d3e7f328016ff1bbbc3a50723268f14277fb1",true,""] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_6_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_6_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_6_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_6_1775311493"] +[2026-04-04 10:04:53] [INFO ] [main.c:354] [didactyl] kind10002 query received event_count=1 +[2026-04-04 10:04:53] [INFO ] [main.c:451] [didactyl] kind10002 query extracted relays=["wss://relay.damus.io","wss://relay.primal.net"] +[2026-04-04 10:04:53] [INFO ] [main.c:556] [didactyl] kind10002 wait success: relay_count=2 +[2026-04-04 10:04:53] [INFO ] [main.c:558] [didactyl] kind10002 relay[0]=wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [main.c:558] [didactyl] kind10002 relay[1]=wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [main.c:562] [didactyl] kind10002 wait success relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":0,"events_published":6,"events_published_ok":5,"events_published_failed":1,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493,"last_publish_error":"rate-limited: you are noting too much"},{"url":"wss://relay.primal.net","status":"connected","events_received":4,"events_published":6,"events_published_ok":6,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775311491,"last_event_time":1775311493}]} +[2026-04-04 10:04:53] [INFO ] [main.c:255] [didactyl] startup checklist [11] Discover self relay list via kind 10002: ok (kind10002=2 added=0 connected=2/2) +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [12] Subscribe admin context: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1366] [didactyl] startup phase: subscribe admin context begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_7_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_7_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_8_1775311493", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_8_1775311493", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2352] [didactyl] admin context subscriptions active for admin 254d7a7f68b4443f... +[2026-04-04 10:04:53] [INFO ] [main.c:1370] [didactyl] startup phase: subscribe admin context end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [12] Subscribe admin context: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [13] Subscribe agent self context: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1374] [didactyl] startup phase: subscribe agent self context begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_9_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_9_1775311493", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_10_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_10_1775311493", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2440] [didactyl] agent self-context subscriptions active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:04:53] [INFO ] [main.c:1378] [didactyl] startup phase: subscribe agent self context end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [13] Subscribe agent self context: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [14] Subscribe self-skill cache: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1382] [didactyl] startup phase: subscribe self skill cache begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_11_1775311493", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_11_1775311493", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_12_1775311493", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_12_1775311493", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2532] [didactyl] self/admin skill subscriptions active (self=1eb171136dbb1ba6..., admin=254d7a7f68b4443f...) +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_7_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_7_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_8_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_8_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_9_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_9_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_10_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_10_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 via wss://relay.damus.io +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 via wss://relay.primal.net +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 via wss://relay.damus.io +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 via wss://relay.primal.net +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_11_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_11_1775311493"] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:1725] [didactyl] self-skill EOSE received: cached skill events=0 +[2026-04-04 10:04:53] [INFO ] [main.c:44] [didactyl] deferred trigger load begin after self-skill EOSE (event_count=0) +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:04:53] [INFO ] [trigger_manager.c:918] [didactyl] trigger manager loaded 1 trigger(s) from self skills (considered=1) +[2026-04-04 10:04:53] [INFO ] [main.c:54] [didactyl] deferred trigger load status: {"success":true,"count":1,"active":1,"triggers":[{"skill_d_tag":"identity_and_rules","filter_json":"{\"from\":\"admin\"}","type":"dm","action":"llm","enabled":true,"subscribed":false,"llm":"","tools":"","has_max_tokens":false,"has_temperature":false,"has_seed":false,"last_fired":0,"last_seen_created_at":0}]} +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=31124 d_tag=identity_and_rules id=b3095dd390e3a08ff24404195292ed302711334687a7de5ca0b433d9fe66a92e created_at=1775311493 +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=10123 d_tag=- id=952130858723506c1eac58e94f253c8b35e137c056dcedf0619ab63348f43caa created_at=1775311493 +[2026-04-04 10:04:53] [INFO ] [main.c:1449] [didactyl] startup phase: subscribe self skill cache end +[2026-04-04 10:04:53] [INFO ] [main.c:255] [didactyl] startup checklist [14] Subscribe self-skill cache: ok (skills=1 adoptions=1) +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [15] Subscribe DMs: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1453] [didactyl] startup phase: subscribe DMs begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_13_1775311493", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 100 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_13_1775311493", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 100 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2643] [didactyl] DM subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:2644] [didactyl] DEBUG DM subscription g_start_time=1775311491 now=1775311493 delta=2 relay_count=2 +[2026-04-04 10:04:53] [INFO ] [main.c:1468] [didactyl] startup phase: subscribe DMs end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [15] Subscribe DMs: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [16] Subscribe wallet events: begin +[2026-04-04 10:04:53] [INFO ] [main.c:1472] [didactyl] startup phase: subscribe wallet events begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_14_1775311493", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 500 + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_14_1775311493", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775311491, + "limit": 500 + }] +[2026-04-04 10:04:53] [INFO ] [nostr_handler.c:2696] [didactyl] wallet event subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:04:53] [INFO ] [main.c:1476] [didactyl] startup phase: subscribe wallet events end +[2026-04-04 10:04:53] [INFO ] [main.c:258] [didactyl] startup checklist [16] Subscribe wallet events: ok +[2026-04-04 10:04:53] [INFO ] [main.c:248] [didactyl] startup checklist [17] Initialize cashu wallet: begin +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_15_1775311493", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_15_1775311493", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_12_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_12_1775311493"] +[2026-04-04 10:04:53] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_13_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_13_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_14_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_14_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_15_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_15_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_15_1775311493"] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_15_1775311493"] +[2026-04-04 10:04:54] [WARN ] [main.c:1492] [didactyl] startup phase: cashu wallet load/create deferred to first tool call +[2026-04-04 10:04:54] [INFO ] [main.c:255] [didactyl] startup checklist [17] Initialize cashu wallet: ok (initialized; load/create deferred) +[2026-04-04 10:04:54] [INFO ] [http_api.c:1570] [didactyl] http api listening on http://127.0.0.1:8485 +[2026-04-04 10:04:54] [INFO ] [main.c:1532] [didactyl] HTTP API listening at http://127.0.0.1:8485 +[2026-04-04 10:04:54] [INFO ] [main.c:1535] [didactyl] HTTP API endpoints: http://127.0.0.1:8485/api/context/current http://127.0.0.1:8485/api/context/parts +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1499] [didactyl] sending plaintext DM content: Didactyl Test Agent has started up and is online at 2026-04-04 10:04:54 (version v0.2.20, connected relays: 2/2). +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:1491] [didactyl] sending encrypted DM event: {"pubkey":"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc","created_at":1775311494,"kind":4,"tags":[["p","254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"]],"content":"jW17RpM/0GksuCPaI3kIdas95grzicaXf5xODF31xSY896PZQuQWRa9VFX3+TPRQuDmv2xLbRw32sox/BAhXasZZe2RFLKPjsHyFoeNBbQB4tRn9fGum/0djr/WL8nK+ppT64wLUPut/2vRFnM+sqv1pZ/nRQe/FFxy8K/VE1eM=?iv=g0c6X4mBxWM04miChtqXTA==","id":"73a9a8e4e3688d46cefb02643ead141fdb7397c632fdd9e6f336c0d31d9ad59c","sig":"08c739b14c9c8482b47dda28f82903eb60ef7fdfd6290b0804f8e4b1c5c7da59b9c82294773bc7394d0adba6474dac7530d36868e65c66f66b9c6e6bda1b6057"} +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:906] [didactyl] publish DM target relays (2): +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.damus.io (async) +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.primal.net (async) +[2026-04-04 10:04:54] [INFO ] [nostr_handler.c:2852] [didactyl] sent DM 73a9a8e4e3688d46... to 254d7a7f68b4443f... via 2 connected relay(s) +[2026-04-04 10:04:54] [INFO ] [main.c:255] [didactyl] startup checklist [18] READY: ok (agent online; entering main poll loop) +[2026-04-04 10:04:54] [INFO ] [main.c:1571] [didactyl] entering main poll loop +[2026-04-04 10:04:54] [INFO ] [main.c:1572] [didactyl] running with pubkey 1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","73a9a8e4e3688d46cefb02643ead141fdb7397c632fdd9e6f336c0d31d9ad59c",true,""] +[2026-04-04 10:04:54] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","73a9a8e4e3688d46cefb02643ead141fdb7397c632fdd9e6f336c0d31d9ad59c",false,"rate-limited: you are noting too much"] +[2026-04-04 10:04:54] [INFO ] [llm.c:76] [didactyl] llm request: method=POST url=https://api.ppq.ai/chat/completions body_bytes=27553 body_preview={"model":"claude-haiku-4.5","max_tokens":10000,"temperature":0.7,"messages":[{"role":"system","content":"# Didactyl Agent\n\n## Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed."},{"role":"user","content":"Look up your own Nostr profile"},{"role":"user","content":"Look up your own Nostr profile","_ts":1775311494}],"tools":[{"type":"function","function":{"name":"nostr_post","description":"Publish a Nostr event to connected relays","parameters":{"type":"object","properties":{"kind":{"type":"integer","description":"Nostr event kind number (for example 1 for note, 7 for reaction, 30023 for longform)"},"content":{"type":"string","description":"Event content/body text to publish"},"tags":{"type":"array","description":"Optional Nostr tags array, e.g. [[\"d\",\"my-skill\"],[\"t\",\"nostr\"]]","items":{"type":"array","items":{"type":"string"}}}},"required":["kind","content"]}}},{"type":"function","function":{"name":"nostr_query","description":"Query events from relays using a Nostr filter","parameters":{"type":"object","properties":{"filter":{"type":"object","description":"Nostr filter object (authors, kinds, ids, since, until, limit, and tag filters)"},"timeout_ms":{"type":"integer","description":"Optional query timeout in milliseconds"}},"required":["filter"]}}},{"type":"function","function":{"name":"local_shell_exec","description":"Execute a shell command and return stdout/stderr","parameters":{"type":"object","properties":{"command":{"type":"string","description":"Shell command to execute in the configured working directory"}},"required":["command"]}}},{"type":"function","function":{"name":"local_file_read","description":"Read a local file as text from the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to read (relative to configured working directory)"},"max_bytes":{"type":"integer","description":"Optional maximum bytes to return"}},"required":["path"]}}},{"type":"function","function":{"name":"local_file_write","description":"Write text content to a local file in the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to write (relative to configured working directory)"},"content":{"type":"string","description":"Text content to write"},"append":{"type":"boolean","description":"When true, append to file instead of overwriting"}},"required":["path","content"]}}},{"type":"function","function":{"name":"nostr_post_readme","description":"Publish README.md as kind 30023 with deterministic d tag readme.md","parameters":{"type":"object","properties":{},"required":[]}}},{"type":"function","function":{"name":"nostr_delete","description":"Request deletion of one or more previously published events (NIP-09 kind 5)","parameters":{"type":"object","properties":{"event_ids":{"type":"array","description":"Event IDs to request deletion for","items":{"type":"string"}},"kinds":{"type":"array","description":"Optional kinds corresponding to event_ids entries","items":{"type":"integer"}},"reason":{"type":"string","description":"Optional human-readable reason"}},"required":["event_ids"]}}},{"type":"function","function":{"name":"nostr_react","description":"React to a Nostr event with like/dislike/emoji (NIP-25 kind 7)","parameters":{"type":"object","properties":{"event_id":{"type":"string","description":"Target event ID to react to"},"event_pubkey":{"type":"string","description":"Author pubkey of target event"},"event_kind":{"type":"integer","description":"Optional kind of target event"},"reaction":{"type":"string","description":"Reaction content such as +, -, ❤️, or emoji"}},"required":["event_id","event_pubkey"]}}},{"type":"function","function":{"name":"nostr_profile_get","description":"Look up a Nostr profile (kind 0 metadata) by pubkey","parameters":{"type":"object","properties":{"pubkey":{"type":"string","description":"Hex pubkey of the profile to fetch"}},"require... +[2026-04-04 10:04:58] [INFO ] [llm.c:76] [didactyl] llm request: method=POST url=https://api.ppq.ai/chat/completions body_bytes=28078 body_preview={"model":"claude-haiku-4.5","max_tokens":10000,"temperature":0.7,"messages":[{"role":"system","content":"# Didactyl Agent\n\n## Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed."},{"role":"user","content":"Look up your own Nostr profile"},{"role":"user","content":"Look up your own Nostr profile","_ts":1775311494},{"role":"assistant","content":"I'll look up my own Nostr profile by first getting my pubkey and then fetching the profile metadata.","tool_calls":[{"id":"toolu_bdrk_0163qj8DFnDXcpoJdjkF4jYj","type":"function","function":{"name":"nostr_pubkey","arguments":"{}"}}]},{"role":"tool","tool_call_id":"toolu_bdrk_0163qj8DFnDXcpoJdjkF4jYj","content":"{\"success\":true,\"pubkey\":\"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc\",\"content\":\"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc\"}"}],"tools":[{"type":"function","function":{"name":"nostr_post","description":"Publish a Nostr event to connected relays","parameters":{"type":"object","properties":{"kind":{"type":"integer","description":"Nostr event kind number (for example 1 for note, 7 for reaction, 30023 for longform)"},"content":{"type":"string","description":"Event content/body text to publish"},"tags":{"type":"array","description":"Optional Nostr tags array, e.g. [[\"d\",\"my-skill\"],[\"t\",\"nostr\"]]","items":{"type":"array","items":{"type":"string"}}}},"required":["kind","content"]}}},{"type":"function","function":{"name":"nostr_query","description":"Query events from relays using a Nostr filter","parameters":{"type":"object","properties":{"filter":{"type":"object","description":"Nostr filter object (authors, kinds, ids, since, until, limit, and tag filters)"},"timeout_ms":{"type":"integer","description":"Optional query timeout in milliseconds"}},"required":["filter"]}}},{"type":"function","function":{"name":"local_shell_exec","description":"Execute a shell command and return stdout/stderr","parameters":{"type":"object","properties":{"command":{"type":"string","description":"Shell command to execute in the configured working directory"}},"required":["command"]}}},{"type":"function","function":{"name":"local_file_read","description":"Read a local file as text from the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to read (relative to configured working directory)"},"max_bytes":{"type":"integer","description":"Optional maximum bytes to return"}},"required":["path"]}}},{"type":"function","function":{"name":"local_file_write","description":"Write text content to a local file in the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to write (relative to configured working directory)"},"content":{"type":"string","description":"Text content to write"},"append":{"type":"boolean","description":"When true, append to file instead of overwriting"}},"required":["path","content"]}}},{"type":"function","function":{"name":"nostr_post_readme","description":"Publish README.md as kind 30023 with deterministic d tag readme.md","parameters":{"type":"object","properties":{},"required":[]}}},{"type":"function","function":{"name":"nostr_delete","description":"Request deletion of one or more previously published events (NIP-09 kind 5)","parameters":{"type":"object","properties":{"event_ids":{"type":"array","description":"Event IDs to request deletion for","items":{"type":"string"}},"kinds":{"type":"array","description":"Optional kinds corresponding to event_ids entries","items":{"type":"integer"}},"reason":{"type":"string","description":"Optional human-readable reason"}},"required":["event_ids"]}}},{"type":"function","function":{"name":"nostr_react","description":"React to a Nostr event with like/dislike/emoji (NIP-25 kind 7)","parameters":{"type":"object","properties":{"event_id":{"type":"string","description":"Target event ID to react to"},"event_pubkey":{"type":"s... +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_16_1775311511", { + "kinds": [0], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_16_1775311511", { + "kinds": [0], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_16_1775311511"] +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_16_1775311511"] +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_16_1775311511"] +[2026-04-04 10:05:11] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_16_1775311511"] +[2026-04-04 10:05:11] [INFO ] [llm.c:76] [didactyl] llm request: method=POST url=https://api.ppq.ai/chat/completions body_bytes=29027 body_preview={"model":"claude-haiku-4.5","max_tokens":10000,"temperature":0.7,"messages":[{"role":"system","content":"# Didactyl Agent\n\n## Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed."},{"role":"user","content":"Look up your own Nostr profile"},{"role":"user","content":"Look up your own Nostr profile","_ts":1775311494},{"role":"assistant","content":"I'll look up my own Nostr profile by first getting my pubkey and then fetching the profile metadata.","tool_calls":[{"id":"toolu_bdrk_0163qj8DFnDXcpoJdjkF4jYj","type":"function","function":{"name":"nostr_pubkey","arguments":"{}"}}]},{"role":"tool","tool_call_id":"toolu_bdrk_0163qj8DFnDXcpoJdjkF4jYj","content":"{\"success\":true,\"pubkey\":\"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc\",\"content\":\"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc\"}"},{"role":"assistant","content":"Now let me fetch my profile metadata:","tool_calls":[{"id":"toolu_bdrk_01SvmURpNtVuQN363Gvcgsrc","type":"function","function":{"name":"nostr_profile_get","arguments":"{\"pubkey\": \"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc\"}"}}]},{"role":"tool","tool_call_id":"toolu_bdrk_01SvmURpNtVuQN363Gvcgsrc","content":"{\"success\":true,\"found\":true,\"event\":{\"content\":\"{\\\"name\\\":\\\"Didactyl Test Agent\\\",\\\"about\\\":\\\"Automated test instance\\\"}\",\"created_at\":1775311493,\"id\":\"98b892b33ce3731cbeb82d791f5e41c7deade4bb61756ed600a14f3aeaa7d45e\",\"kind\":0,\"pubkey\":\"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc\",\"sig\":\"b9d083081f36b1be3c60066af6680050fbbdc42d766277f534fd096f81279e5b70f1083a309a3b96e7065bd3c89c4682cf89cc17968d29b59ac6e062c13109bd\",\"tags\":[]},\"profile\":{\"name\":\"Didactyl Test Agent\",\"about\":\"Automated test instance\"}}"}],"tools":[{"type":"function","function":{"name":"nostr_post","description":"Publish a Nostr event to connected relays","parameters":{"type":"object","properties":{"kind":{"type":"integer","description":"Nostr event kind number (for example 1 for note, 7 for reaction, 30023 for longform)"},"content":{"type":"string","description":"Event content/body text to publish"},"tags":{"type":"array","description":"Optional Nostr tags array, e.g. [[\"d\",\"my-skill\"],[\"t\",\"nostr\"]]","items":{"type":"array","items":{"type":"string"}}}},"required":["kind","content"]}}},{"type":"function","function":{"name":"nostr_query","description":"Query events from relays using a Nostr filter","parameters":{"type":"object","properties":{"filter":{"type":"object","description":"Nostr filter object (authors, kinds, ids, since, until, limit, and tag filters)"},"timeout_ms":{"type":"integer","description":"Optional query timeout in milliseconds"}},"required":["filter"]}}},{"type":"function","function":{"name":"local_shell_exec","description":"Execute a shell command and return stdout/stderr","parameters":{"type":"object","properties":{"command":{"type":"string","description":"Shell command to execute in the configured working directory"}},"required":["command"]}}},{"type":"function","function":{"name":"local_file_read","description":"Read a local file as text from the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to read (relative to configured working directory)"},"max_bytes":{"type":"integer","description":"Optional maximum bytes to return"}},"required":["path"]}}},{"type":"function","function":{"name":"local_file_write","description":"Write text content to a local file in the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to write (relative to configured working directory)"},"content":{"type":"string","description":"Text content to write"},"append":{"type":"boolean","description":"When true, append to file instead of overwriting"}},"required":["path","content"]}}},{"type":"function","function":{"name":"nost... +[2026-04-04 10:05:16] [INFO ] [llm.c:336] [didactyl] llm request sanitizer: removed 2 empty text message(s) before provider request +[2026-04-04 10:05:16] [INFO ] [llm.c:76] [didactyl] llm request: method=POST url=https://api.ppq.ai/chat/completions body_bytes=27418 body_preview={"model":"claude-haiku-4.5","max_tokens":10000,"temperature":0.7,"messages":[{"role":"system","content":"# Didactyl Agent\n\n## Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed."}],"tools":[{"type":"function","function":{"name":"nostr_post","description":"Publish a Nostr event to connected relays","parameters":{"type":"object","properties":{"kind":{"type":"integer","description":"Nostr event kind number (for example 1 for note, 7 for reaction, 30023 for longform)"},"content":{"type":"string","description":"Event content/body text to publish"},"tags":{"type":"array","description":"Optional Nostr tags array, e.g. [[\"d\",\"my-skill\"],[\"t\",\"nostr\"]]","items":{"type":"array","items":{"type":"string"}}}},"required":["kind","content"]}}},{"type":"function","function":{"name":"nostr_query","description":"Query events from relays using a Nostr filter","parameters":{"type":"object","properties":{"filter":{"type":"object","description":"Nostr filter object (authors, kinds, ids, since, until, limit, and tag filters)"},"timeout_ms":{"type":"integer","description":"Optional query timeout in milliseconds"}},"required":["filter"]}}},{"type":"function","function":{"name":"local_shell_exec","description":"Execute a shell command and return stdout/stderr","parameters":{"type":"object","properties":{"command":{"type":"string","description":"Shell command to execute in the configured working directory"}},"required":["command"]}}},{"type":"function","function":{"name":"local_file_read","description":"Read a local file as text from the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to read (relative to configured working directory)"},"max_bytes":{"type":"integer","description":"Optional maximum bytes to return"}},"required":["path"]}}},{"type":"function","function":{"name":"local_file_write","description":"Write text content to a local file in the configured working directory","parameters":{"type":"object","properties":{"path":{"type":"string","description":"Path to file to write (relative to configured working directory)"},"content":{"type":"string","description":"Text content to write"},"append":{"type":"boolean","description":"When true, append to file instead of overwriting"}},"required":["path","content"]}}},{"type":"function","function":{"name":"nostr_post_readme","description":"Publish README.md as kind 30023 with deterministic d tag readme.md","parameters":{"type":"object","properties":{},"required":[]}}},{"type":"function","function":{"name":"nostr_delete","description":"Request deletion of one or more previously published events (NIP-09 kind 5)","parameters":{"type":"object","properties":{"event_ids":{"type":"array","description":"Event IDs to request deletion for","items":{"type":"string"}},"kinds":{"type":"array","description":"Optional kinds corresponding to event_ids entries","items":{"type":"integer"}},"reason":{"type":"string","description":"Optional human-readable reason"}},"required":["event_ids"]}}},{"type":"function","function":{"name":"nostr_react","description":"React to a Nostr event with like/dislike/emoji (NIP-25 kind 7)","parameters":{"type":"object","properties":{"event_id":{"type":"string","description":"Target event ID to react to"},"event_pubkey":{"type":"string","description":"Author pubkey of target event"},"event_kind":{"type":"integer","description":"Optional kind of target event"},"reaction":{"type":"string","description":"Reaction content such as +, -, ❤️, or emoji"}},"required":["event_id","event_pubkey"]}}},{"type":"function","function":{"name":"nostr_profile_get","description":"Look up a Nostr profile (kind 0 metadata) by pubkey","parameters":{"type":"object","properties":{"pubkey":{"type":"string","description":"Hex pubkey of the profile to fetch"}},"required":["pubkey"]}}},{"type":"function","function":{"name":"nostr_relay_status","description":"Get connection status and statistics for all... diff --git a/tests/results/20260404T140236Z/runtime_test_genesis.jsonc b/tests/results/20260404T140236Z/runtime_test_genesis.jsonc new file mode 100644 index 0000000..8aa613f --- /dev/null +++ b/tests/results/20260404T140236Z/runtime_test_genesis.jsonc @@ -0,0 +1,53 @@ +{ + // TEST CONFIGURATION + // Use disposable keys/accounts only. + "key": { + "nsec": "d3011e59954403fb84729ab0e9fb239ba0f0238b3f353dfb5c08a32f4b3d21a1" + }, + "admin": { + "pubkey": "254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac" + }, + "dm_protocol": "nip04", + "llm": { + "provider": "https://api.ppq.ai", + "api_key": "sk-SBI2Pga0J6n8jpPorHbRHC", + "model": "claude-haiku-4.5", + "base_url": "https://api.ppq.ai", + "max_tokens": 10000, + "temperature": 0.7 + }, + "api": { + "enabled": true, + "port": 8485, + "bind_address": "127.0.0.1" + }, + "startup_events": [ + { + "kind": 0, + "content_fields": { + "name": "Didactyl Test Agent", + "about": "Automated test instance" + }, + "tags": [] + }, + { + "kind": 10002, + "content": "", + "tags": [ + ["r", "wss://relay.damus.io"], + ["r", "wss://relay.primal.net"] + ] + }, + { + "kind": 31124, + "content": "# Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed.", + "tags": [ + ["d", "identity_and_rules"], + ["app", "didactyl"], + ["scope", "private"], + ["trigger", "dm"], + ["filter", "{\"from\":\"admin\"}"] + ] + } + ] +} diff --git a/tests/results/20260404T140545Z/agent_debug.log b/tests/results/20260404T140545Z/agent_debug.log new file mode 100644 index 0000000..c6ab66d --- /dev/null +++ b/tests/results/20260404T140545Z/agent_debug.log @@ -0,0 +1,396 @@ +[2026-04-04 10:29:17] [INFO ] [main.c:1016] Didactyl v0.2.20 starting +[2026-04-04 10:29:17] [INFO ] [nostr_handler.c:2207] [didactyl] initializing relay pool with 2 relays +[2026-04-04 10:29:17] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.damus.io +[2026-04-04 10:29:17] [INFO ] [nostr_handler.c:2231] [didactyl] added relay: wss://relay.primal.net +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_1_1775312957", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_1_1775312957", { + "kinds": [10000], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_1_1775312957"] +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_1_1775312957"] +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_1_1775312957"] +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_1_1775312957"] +[2026-04-04 10:29:18] [INFO ] [main.c:248] [didactyl] startup checklist [01] Relay connectivity: begin +[2026-04-04 10:29:18] [INFO ] [main.c:255] [didactyl] startup checklist [01] Relay connectivity: ok (connected relays: 2/2) +[2026-04-04 10:29:18] [INFO ] [main.c:248] [didactyl] startup checklist [02] Recover runtime config from Nostr: begin +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_2_1775312958", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 10:29:18] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_2_1775312958", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["agent_config"], + "limit": 1 + }] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_2_1775312958"] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_2_1775312958"] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_2_1775312958"] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_2_1775312958"] +[2026-04-04 10:29:19] [INFO ] [main.c:258] [didactyl] startup checklist [02] Recover runtime config from Nostr: ok +[2026-04-04 10:29:19] [INFO ] [main.c:248] [didactyl] startup checklist [03] Validate LLM config: begin +[2026-04-04 10:29:19] [INFO ] [main.c:258] [didactyl] startup checklist [03] Validate LLM config: ok +[2026-04-04 10:29:19] [INFO ] [main.c:248] [didactyl] startup checklist [04] Validate admin config: begin +[2026-04-04 10:29:19] [INFO ] [main.c:258] [didactyl] startup checklist [04] Validate admin config: ok +[2026-04-04 10:29:19] [INFO ] [main.c:248] [didactyl] startup checklist [05] Initialize LLM client: begin +[2026-04-04 10:29:19] [INFO ] [main.c:258] [didactyl] startup checklist [05] Initialize LLM client: ok +[2026-04-04 10:29:19] [INFO ] [main.c:248] [didactyl] startup checklist [06] Detect first run via kind 10002: begin +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_3_1775312959", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_3_1775312959", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_3_1775312959"] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_3_1775312959"] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_3_1775312959"] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_3_1775312959"] +[2026-04-04 10:29:19] [INFO ] [main.c:1245] [didactyl] startup phase: first-run detection via kind 10002 => subsequent-run +[2026-04-04 10:29:19] [INFO ] [main.c:255] [didactyl] startup checklist [06] Detect first run via kind 10002: ok (subsequent-run) +[2026-04-04 10:29:19] [INFO ] [main.c:248] [didactyl] startup checklist [07] Reconcile/persist startup state: begin +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_4_1775312959", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:29:19] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_4_1775312959", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 1 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_4_1775312959"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_4_1775312959"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_4_1775312959"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_4_1775312959"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.damus.io (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 0 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10002 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 31124 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 10123 event published to wss://relay.primal.net (async, reason=startup_reconcile) +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=0 (profile) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10002 (relay_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=31124 (skill) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:3285] [didactyl] startup publish detail: kind=10123 (adoption_list) -> wss://relay.damus.io, wss://relay.primal.net +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:3297] [didactyl] startup publish summary: 4/4 event(s) published to at least one relay; relays used=2/2 +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:906] [didactyl] publish kind event target relays (2): +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.damus.io (async, reason=manual_publish) +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2949] [didactyl] kind 30078 event published to wss://relay.primal.net (async, reason=manual_publish) +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:3012] [didactyl] published kind 30078 event via 2 connected relay(s) +[2026-04-04 10:29:20] [INFO ] [main.c:1267] [didactyl] startup phase: persisted encrypted runtime config to Nostr +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [07] Reconcile/persist startup state: ok +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [08] Initialize agent: begin +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_5_1775312960", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_5_1775312960", { + "kinds": [30078], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "#d": ["memory"], + "limit": 1 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","11bc4eb1ff5e51d8df325c1f16eab167535bd04f4768129437098aef02917a0c",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","5e774283a8db088821e3c20d7f5ec2dbc64e9ca32d5342ce31f699205e16b22c",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","5e774283a8db088821e3c20d7f5ec2dbc64e9ca32d5342ce31f699205e16b22c",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","e863ae73ffdb98391429ecbb36d4ad931ca994804cd673a310ed91f94806a795",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","9f540b534baeaa37d5bdfd1b3ef6ef1f9b65b8f00236ad28d63e098b56e56c35",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","11bc4eb1ff5e51d8df325c1f16eab167535bd04f4768129437098aef02917a0c",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","e863ae73ffdb98391429ecbb36d4ad931ca994804cd673a310ed91f94806a795",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","9f540b534baeaa37d5bdfd1b3ef6ef1f9b65b8f00236ad28d63e098b56e56c35",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_5_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_5_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_5_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_5_1775312960"] +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [08] Initialize agent: ok +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [09] Initialize trigger manager: begin +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:814] [didactyl] trigger manager initialized (capacity=16) +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [09] Initialize trigger manager: ok +[2026-04-04 10:29:20] [INFO ] [main.c:1308] [didactyl] startup phase: deferred trigger load will run after self-skill EOSE +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [10] Load startup triggers: begin +[2026-04-04 10:29:20] [INFO ] [main.c:1311] [didactyl] startup phase: startup-config trigger load begin +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:1138] [didactyl] trigger added d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:1021] [didactyl] startup trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:1029] [didactyl] trigger manager loaded 1 trigger(s) from startup config (considered=1) +[2026-04-04 10:29:20] [INFO ] [main.c:1315] [didactyl] startup phase: startup-config trigger load end +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [10] Load startup triggers: ok +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [11] Discover self relay list via kind 10002: begin +[2026-04-04 10:29:20] [INFO ] [main.c:327] [didactyl] kind10002 query begin: timeout_ms=5000 author=1eb171136dbb1ba6... relay_count=2 +[2026-04-04 10:29:20] [INFO ] [main.c:334] [didactyl] kind10002 query relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":1,"events_published":6,"events_published_ok":4,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775312957,"last_event_time":1775312960},{"url":"wss://relay.primal.net","status":"connected","events_received":3,"events_published":6,"events_published_ok":4,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775312957,"last_event_time":1775312960}]} +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_6_1775312960", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_6_1775312960", { + "kinds": [10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 16 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","86a6101b5cd0fc5e04cd22ad7c897cecd8b4a6a827180b1a18d1da10c31ce9bf",false,"rate-limited: you are noting too much"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","59d0b381dcf93eb42a65bc1bbc31e54b82ac20bb3b2dd654b08d3780da41ee07",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","59d0b381dcf93eb42a65bc1bbc31e54b82ac20bb3b2dd654b08d3780da41ee07",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","86a6101b5cd0fc5e04cd22ad7c897cecd8b4a6a827180b1a18d1da10c31ce9bf",true,""] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_6_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_6_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_6_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_6_1775312960"] +[2026-04-04 10:29:20] [INFO ] [main.c:354] [didactyl] kind10002 query received event_count=1 +[2026-04-04 10:29:20] [INFO ] [main.c:451] [didactyl] kind10002 query extracted relays=["wss://relay.damus.io","wss://relay.primal.net"] +[2026-04-04 10:29:20] [INFO ] [main.c:556] [didactyl] kind10002 wait success: relay_count=2 +[2026-04-04 10:29:20] [INFO ] [main.c:558] [didactyl] kind10002 relay[0]=wss://relay.damus.io +[2026-04-04 10:29:20] [INFO ] [main.c:558] [didactyl] kind10002 relay[1]=wss://relay.primal.net +[2026-04-04 10:29:20] [INFO ] [main.c:562] [didactyl] kind10002 wait success relay snapshot: {"relay_count":2,"connected_count":2,"relays":[{"url":"wss://relay.damus.io","status":"connected","events_received":1,"events_published":6,"events_published_ok":5,"events_published_failed":1,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775312957,"last_event_time":1775312960,"last_publish_error":"rate-limited: you are noting too much"},{"url":"wss://relay.primal.net","status":"connected","events_received":4,"events_published":6,"events_published_ok":6,"events_published_failed":0,"ping_latency_current":0,"ping_latency_avg":0,"query_latency_avg":0,"publish_latency_avg":0,"connection_uptime_start":1775312957,"last_event_time":1775312960}]} +[2026-04-04 10:29:20] [INFO ] [main.c:255] [didactyl] startup checklist [11] Discover self relay list via kind 10002: ok (kind10002=2 added=0 connected=2/2) +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [12] Subscribe admin context: begin +[2026-04-04 10:29:20] [INFO ] [main.c:1366] [didactyl] startup phase: subscribe admin context begin +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_7_1775312960", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_7_1775312960", { + "kinds": [0, 3, 10002], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 32 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_8_1775312960", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_8_1775312960", { + "kinds": [1], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 10 + }] +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2352] [didactyl] admin context subscriptions active for admin 254d7a7f68b4443f... +[2026-04-04 10:29:20] [INFO ] [main.c:1370] [didactyl] startup phase: subscribe admin context end +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [12] Subscribe admin context: ok +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [13] Subscribe agent self context: begin +[2026-04-04 10:29:20] [INFO ] [main.c:1374] [didactyl] startup phase: subscribe agent self context begin +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_9_1775312960", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_9_1775312960", { + "kinds": [0, 3, 10002], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 64 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_10_1775312960", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_10_1775312960", { + "kinds": [1], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 10 + }] +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2440] [didactyl] agent self-context subscriptions active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:29:20] [INFO ] [main.c:1378] [didactyl] startup phase: subscribe agent self context end +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [13] Subscribe agent self context: ok +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [14] Subscribe self-skill cache: begin +[2026-04-04 10:29:20] [INFO ] [main.c:1382] [didactyl] startup phase: subscribe self skill cache begin +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_11_1775312960", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_11_1775312960", { + "kinds": [31123, 31124, 10123], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "limit": 300 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_12_1775312960", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_12_1775312960", { + "kinds": [31123], + "authors": ["254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"], + "limit": 150 + }] +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2532] [didactyl] self/admin skill subscriptions active (self=1eb171136dbb1ba6..., admin=254d7a7f68b4443f...) +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_7_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_7_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_8_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_8_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_9_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_9_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_10_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_10_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=9f540b534baeaa37d5bdfd1b3ef6ef1f9b65b8f00236ad28d63e098b56e56c35 created_at=1775312960 via wss://relay.primal.net +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31124 d_tag=identity_and_rules id=9f540b534baeaa37d5bdfd1b3ef6ef1f9b65b8f00236ad28d63e098b56e56c35 created_at=1775312960 via wss://relay.damus.io +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:1207] [didactyl] live self-skill trigger registered d_tag=identity_and_rules action=llm enabled=1 +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=11bc4eb1ff5e51d8df325c1f16eab167535bd04f4768129437098aef02917a0c created_at=1775312960 via wss://relay.primal.net +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=10123 d_tag=- id=11bc4eb1ff5e51d8df325c1f16eab167535bd04f4768129437098aef02917a0c created_at=1775312960 via wss://relay.damus.io +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] RECV +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2151] [didactyl] self-skill cache received kind=31123 d_tag=test-harness-probe id=fbf278b5a6ccd7fd86b0c8c7bfb787aa3f94c578e0bda74241e1d8e613394467 created_at=1775312616 via wss://relay.primal.net +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_11_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_11_1775312960"] +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:1725] [didactyl] self-skill EOSE received: cached skill events=0 +[2026-04-04 10:29:20] [INFO ] [main.c:44] [didactyl] deferred trigger load begin after self-skill EOSE (event_count=0) +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:1364] [didactyl] trigger updated d_tag=identity_and_rules action=0 enabled=1 +[2026-04-04 10:29:20] [INFO ] [trigger_manager.c:918] [didactyl] trigger manager loaded 1 trigger(s) from self skills (considered=2) +[2026-04-04 10:29:20] [INFO ] [main.c:54] [didactyl] deferred trigger load status: {"success":true,"count":1,"active":1,"triggers":[{"skill_d_tag":"identity_and_rules","filter_json":"{\"from\":\"admin\"}","type":"dm","action":"llm","enabled":true,"subscribed":false,"llm":"","tools":"","has_max_tokens":false,"has_temperature":false,"has_seed":false,"last_fired":0,"last_seen_created_at":0}]} +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=31124 d_tag=identity_and_rules id=9f540b534baeaa37d5bdfd1b3ef6ef1f9b65b8f00236ad28d63e098b56e56c35 created_at=1775312960 +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=10123 d_tag=- id=11bc4eb1ff5e51d8df325c1f16eab167535bd04f4768129437098aef02917a0c created_at=1775312960 +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2757] [didactyl] self-skill cache event: kind=31123 d_tag=test-harness-probe id=fbf278b5a6ccd7fd86b0c8c7bfb787aa3f94c578e0bda74241e1d8e613394467 created_at=1775312616 +[2026-04-04 10:29:20] [INFO ] [main.c:1449] [didactyl] startup phase: subscribe self skill cache end +[2026-04-04 10:29:20] [INFO ] [main.c:255] [didactyl] startup checklist [14] Subscribe self-skill cache: ok (skills=2 adoptions=1) +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [15] Subscribe DMs: begin +[2026-04-04 10:29:20] [INFO ] [main.c:1453] [didactyl] startup phase: subscribe DMs begin +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_13_1775312960", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775312957, + "limit": 100 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_13_1775312960", { + "kinds": [4], + "#p": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775312957, + "limit": 100 + }] +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2643] [didactyl] DM subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:2644] [didactyl] DEBUG DM subscription g_start_time=1775312957 now=1775312960 delta=3 relay_count=2 +[2026-04-04 10:29:20] [INFO ] [main.c:1468] [didactyl] startup phase: subscribe DMs end +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [15] Subscribe DMs: ok +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [16] Subscribe wallet events: begin +[2026-04-04 10:29:20] [INFO ] [main.c:1472] [didactyl] startup phase: subscribe wallet events begin +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_14_1775312960", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775312957, + "limit": 500 + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_14_1775312960", { + "kinds": [17375, 7375, 5], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"], + "since": 1775312957, + "limit": 500 + }] +[2026-04-04 10:29:20] [INFO ] [nostr_handler.c:2696] [didactyl] wallet event subscription active for pubkey 1eb171136dbb1ba6... +[2026-04-04 10:29:20] [INFO ] [main.c:1476] [didactyl] startup phase: subscribe wallet events end +[2026-04-04 10:29:20] [INFO ] [main.c:258] [didactyl] startup checklist [16] Subscribe wallet events: ok +[2026-04-04 10:29:20] [INFO ] [main.c:248] [didactyl] startup checklist [17] Initialize cashu wallet: begin +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["REQ", "pool_15_1775312960", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["REQ", "pool_15_1775312960", { + "kinds": [17375, 7375], + "authors": ["1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc"] + }] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_12_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_12_1775312960"] +[2026-04-04 10:29:20] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_13_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_13_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:1718] [didactyl] DEBUG on_eose called: event_count=0 +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_14_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_14_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["EOSE","pool_15_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["EOSE","pool_15_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_15_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_15_1775312960"] +[2026-04-04 10:29:21] [WARN ] [main.c:1492] [didactyl] startup phase: cashu wallet load/create deferred to first tool call +[2026-04-04 10:29:21] [INFO ] [main.c:255] [didactyl] startup checklist [17] Initialize cashu wallet: ok (initialized; load/create deferred) +[2026-04-04 10:29:21] [INFO ] [http_api.c:1570] [didactyl] http api listening on http://127.0.0.1:8485 +[2026-04-04 10:29:21] [INFO ] [main.c:1532] [didactyl] HTTP API listening at http://127.0.0.1:8485 +[2026-04-04 10:29:21] [INFO ] [main.c:1535] [didactyl] HTTP API endpoints: http://127.0.0.1:8485/api/context/current http://127.0.0.1:8485/api/context/parts +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:1499] [didactyl] sending plaintext DM content: Didactyl Test Agent has started up and is online at 2026-04-04 10:29:21 (version v0.2.20, connected relays: 2/2). +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:1491] [didactyl] sending encrypted DM event: {"pubkey":"1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc","created_at":1775312961,"kind":4,"tags":[["p","254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac"]],"content":"TOAe6VwLkGJemoQ1F6TB3lfiGq251GlJjayadl04UGMhnkm/A/PKzaqzzqO5q0Xpn9LM83KjU575uHL+PSEaM76IfvFcMVN8Qa1C3hSF9/7vpn1LuIGBIpQn8fmDpHji+gjRVZk1wvWUPoC63CCTWH/cUgJkILYQaW4XMdJwA1E=?iv=cuZPWOh+jAIjqTR/U47BIg==","id":"d79dcbff5a6b5f37100d1a04e115c64231c28476fa0e21454cdaa30fc5010622","sig":"e356e8244b8930268aab91124db3d3632952b091a6880de13e2a9b1122f538f5fec3b5bffaae2615bcbcb42a3663b85d6f1f66f9f264967c4c5b44009ada977a"} +[2026-04-04 10:29:21] [INFO ] [nostr_handler.c:906] [didactyl] publish DM target relays (2): +[2026-04-04 10:29:21] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.damus.io +[2026-04-04 10:29:21] [INFO ] [nostr_handler.c:908] [didactyl] -> wss://relay.primal.net +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:162] [didactyl] [nostr:websocket] SEND +[2026-04-04 10:29:21] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.damus.io (async) +[2026-04-04 10:29:21] [INFO ] [nostr_handler.c:2843] [didactyl] kind 4 event published to wss://relay.primal.net (async) +[2026-04-04 10:29:21] [INFO ] [nostr_handler.c:2852] [didactyl] sent DM d79dcbff5a6b5f37... to 254d7a7f68b4443f... via 2 connected relay(s) +[2026-04-04 10:29:21] [INFO ] [main.c:255] [didactyl] startup checklist [18] READY: ok (agent online; entering main poll loop) +[2026-04-04 10:29:21] [INFO ] [main.c:1571] [didactyl] entering main poll loop +[2026-04-04 10:29:21] [INFO ] [main.c:1572] [didactyl] running with pubkey 1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.primal.net:443: ["OK","d79dcbff5a6b5f37100d1a04e115c64231c28476fa0e21454cdaa30fc5010622",true,""] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] RECV relay.damus.io:443: ["OK","d79dcbff5a6b5f37100d1a04e115c64231c28476fa0e21454cdaa30fc5010622",false,"rate-limited: you are noting too much"] +[2026-04-04 10:29:21] [INFO ] [main.c:1584] [didactyl] shutting down +[2026-04-04 10:29:21] [INFO ] [trigger_manager.c:1602] [didactyl] trigger manager cleaned up +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_7_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_7_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_8_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_8_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_9_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_9_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_10_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_10_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_11_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_11_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_12_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_12_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_13_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_13_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.damus.io:443: ["CLOSE", "pool_14_1775312960"] +[2026-04-04 10:29:21] [TRACE] [nostr_handler.c:169] [didactyl] [nostr:websocket] SEND relay.primal.net:443: ["CLOSE", "pool_14_1775312960"] diff --git a/tests/results/20260404T140545Z/results.json b/tests/results/20260404T140545Z/results.json new file mode 100644 index 0000000..620071e --- /dev/null +++ b/tests/results/20260404T140545Z/results.json @@ -0,0 +1,494 @@ +{ + "run_meta": { + "run_timestamp": "2026-04-04T14:29:21.419470+00:00", + "base_url": "http://127.0.0.1:8485", + "config": "tests/results/20260404T140545Z/runtime_test_genesis.jsonc", + "binary": "didactyl_static_x86_64_debug", + "test_count": 43 + }, + "results": [ + { + "suite": "test_conversation", + "name": "simple_greeting", + "status": "pass", + "message": "greeting response ok", + "duration_seconds": 4.245289196027443, + "agent_errors": [], + "details": { + "response": "Hello! I'm a test agent built on the Didactyl platform. I don't have a specific personal name, but you can think of me as your Didactyl test agent. \n\nI'm here to help you with various tasks using the " + } + }, + { + "suite": "test_conversation", + "name": "agent_responds_about_itself", + "status": "pass", + "message": "self description ok", + "duration_seconds": 27.058145482034888, + "agent_errors": [], + "details": { + "response": "i'm **didactyl**, a nostr-native ai agent. here's what i do:\n\n**core identity:**\n- i'm an autonomous agent built on the nostr protocol, operating with cryptographic keypairs for identity and authentic" + } + }, + { + "suite": "test_conversation", + "name": "empty_message_handling", + "status": "pass", + "message": "empty message handled", + "duration_seconds": 4.667415744974278, + "agent_errors": [], + "details": { + "success": true + } + }, + { + "suite": "test_conversation", + "name": "very_long_message", + "status": "timeout", + "message": "timed out", + "duration_seconds": 76.51407337700948, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_errors", + "name": "invalid_json_body", + "status": "pass", + "message": "invalid json rejected", + "duration_seconds": 0.3308889329782687, + "agent_errors": [], + "details": { + "status": 400 + } + }, + { + "suite": "test_errors", + "name": "missing_message_field", + "status": "pass", + "message": "missing message rejected", + "duration_seconds": 0.3314382230164483, + "agent_errors": [], + "details": { + "status": 400 + } + }, + { + "suite": "test_errors", + "name": "unknown_endpoint", + "status": "pass", + "message": "unknown endpoint 404", + "duration_seconds": 0.33126351796090603, + "agent_errors": [], + "details": { + "status": 404 + } + }, + { + "suite": "test_errors", + "name": "webhook_nonexistent_dtag", + "status": "pass", + "message": "nonexistent d_tag 404", + "duration_seconds": 0.33133913297206163, + "agent_errors": [], + "details": { + "status": 404 + } + }, + { + "suite": "test_health", + "name": "status_returns_200", + "status": "pass", + "message": "status success", + "duration_seconds": 0.3312724310089834, + "agent_errors": [], + "details": { + "keys": [ + "success", + "name", + "version", + "pubkey", + "relay_count", + "connected_relays", + "active_triggers" + ] + } + }, + { + "suite": "test_health", + "name": "status_has_fields", + "status": "pass", + "message": "required fields present", + "duration_seconds": 0.3312905229977332, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_health", + "name": "context_current_returns_messages", + "status": "pass", + "message": "context current ok", + "duration_seconds": 0.33117285498883575, + "agent_errors": [], + "details": { + "message_count": 2 + } + }, + { + "suite": "test_health", + "name": "context_parts_has_system_prompt", + "status": "pass", + "message": "context parts ok", + "duration_seconds": 0.3312868010252714, + "agent_errors": [], + "details": { + "parts": [ + "system_prompt", + "dm_history" + ] + } + }, + { + "suite": "test_restart", + "name": "clean_restart", + "status": "pass", + "message": "restart succeeded", + "duration_seconds": 3.8490588110289536, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_restart", + "name": "status_after_restart", + "status": "pass", + "message": "status stable after restart", + "duration_seconds": 4.151956218993291, + "agent_errors": [], + "details": { + "pubkey": "1eb171136dbb1ba695b2f14296adfe88a1f81d4220cc7d186b0e139b8ce0cdbc" + } + }, + { + "suite": "test_restart", + "name": "conversation_after_restart", + "status": "pass", + "message": "conversation works post-restart", + "duration_seconds": 7.446270982036367, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_timeouts", + "name": "response_within_timeout", + "status": "pass", + "message": "response within timeout", + "duration_seconds": 3.016901847033296, + "agent_errors": [], + "details": { + "elapsed": 3.0168789690360427 + } + }, + { + "suite": "test_timeouts", + "name": "status_responds_fast", + "status": "pass", + "message": "status fast", + "duration_seconds": 0.32919533003587276, + "agent_errors": [], + "details": { + "elapsed": 0.3291910729603842 + } + }, + { + "suite": "test_timeouts", + "name": "context_responds_fast", + "status": "pass", + "message": "context fast", + "duration_seconds": 0.33155528403585777, + "agent_errors": [], + "details": { + "elapsed": 0.3315501930192113 + } + }, + { + "suite": "test_tools_blossom", + "name": "blossom_list", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.46405867597787, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_cashu", + "name": "wallet_balance", + "status": "pass", + "message": "ok", + "duration_seconds": 7.047360337048303, + "agent_errors": [], + "details": { + "tool_calls": [ + "cashu_wallet_balance" + ], + "final_response": "Your Cashu wallet balance is:\n\n- **Total**: 0 satoshis\n- **Wallet Status**: Not loaded\n- **Entries**: None\n\nThe wallet doesn't currently have any loaded mints or proofs. To start using the wallet, you" + } + }, + { + "suite": "test_tools_identity", + "name": "get_pubkey", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.42999882198637, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_identity", + "name": "get_npub", + "status": "pass", + "message": "ok", + "duration_seconds": 6.77238113101339, + "agent_errors": [], + "details": { + "tool_calls": [ + "nostr_npub" + ], + "final_response": "My npub is: **npub1r6chzymdhvd6d9dj79pfdt073zsls82zyrx86xrtpcfehr8qek7q3rkewf**" + } + }, + { + "suite": "test_tools_identity", + "name": "agent_identity", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.39259405602934, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_identity", + "name": "agent_version", + "status": "pass", + "message": "ok", + "duration_seconds": 6.746906190994196, + "agent_errors": [], + "details": { + "tool_calls": [ + "agent_version" + ], + "final_response": "I'm **Didactyl v0.2.20**, a sovereign AI agent daemon on Nostr. \n\nHere are my details:\n- **Name**: Didactyl\n- **Description**: A sovereign AI agent daemon on Nostr\n- **Version**: v0.2.20 (major: 0, mi" + } + }, + { + "suite": "test_tools_identity", + "name": "admin_identity", + "status": "timeout", + "message": "timed out", + "duration_seconds": 74.42458030197304, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_memory", + "name": "task_list", + "status": "pass", + "message": "ok", + "duration_seconds": 8.36226256104419, + "agent_errors": [], + "details": { + "tool_calls": [ + "task_list" + ], + "final_response": "Here's your current task list:\n\n**Active Tasks:**\n- [ ] **Task 1:** test harness probe (Status: pending)\n - Created: 1775311162\n - Last updated: 1775311162\n\nYou have one pending task in your task me" + } + }, + { + "suite": "test_tools_memory", + "name": "task_manage_add_remove", + "status": "timeout", + "message": "timed out", + "duration_seconds": 84.9701488899882, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_memory", + "name": "memory_save_recall", + "status": "pass", + "message": "ok", + "duration_seconds": 10.4175310000428, + "agent_errors": [], + "details": { + "tool_calls": [ + "memory_save", + "memory_recall" + ], + "final_response": "Perfect! The memory system is working correctly. Here's what happened:\n\n**Memory Save Result:**\n- \u2705 Successfully saved to kind 30078 (encrypted agent config)\n- Event ID: `b4aba2eb45a6b41eec9792fa381cf" + } + }, + { + "suite": "test_tools_nostr", + "name": "nostr_post_kind1", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.96807993302355, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_nostr", + "name": "nostr_query_recent", + "status": "pass", + "message": "ok", + "duration_seconds": 14.233945493993815, + "agent_errors": [], + "details": { + "tool_calls": [ + "nostr_query" + ], + "final_response": "Great! I found the 3 most recent kind 1 notes. Here they are:\n\n## 3 Most Recent Kind 1 Notes\n\n**1. Relaymonitor Latency Test**\n- **Author:** `0b01aa38c2cc9abfbe4a10d54b182793479fb80da14a91d13be38ea555" + } + }, + { + "suite": "test_tools_nostr", + "name": "nostr_my_events", + "status": "timeout", + "message": "timed out", + "duration_seconds": 74.01618752401555, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_nostr", + "name": "nostr_relay_status", + "status": "pass", + "message": "ok", + "duration_seconds": 6.104476057982538, + "agent_errors": [], + "details": { + "tool_calls": [ + "nostr_relay_status" + ], + "final_response": "Great! Here's the status of my relay connections:\n\n**Overall Status:**\n- **Total Relays:** 2\n- **Connected:** 2/2 \u2705\n\n**Detailed Relay Status:**\n\n1. **wss://relay.damus.io**\n - Status: Connected \u2705\n " + } + }, + { + "suite": "test_tools_nostr", + "name": "nostr_dm_send", + "status": "timeout", + "message": "timed out", + "duration_seconds": 74.01507185597438, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_nostr", + "name": "nostr_encode_npub", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.42353967600502, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_nostr", + "name": "nostr_profile_get", + "status": "timeout", + "message": "timed out", + "duration_seconds": 74.39149832999101, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_skills", + "name": "skill_list", + "status": "pass", + "message": "ok", + "duration_seconds": 7.244145753968041, + "agent_errors": [], + "details": { + "tool_calls": [ + "skill_list" + ], + "final_response": "I have **1 available skill** that I'm currently using:\n\n**1. identity_and_rules** (Private - Agent-owned, Adopted)\n - **Scope:** Private\n - **Status:** Adopted and Active\n - **Description:** Thi" + } + }, + { + "suite": "test_tools_skills", + "name": "trigger_list", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.40902369801188, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_skills", + "name": "skill_create_and_remove", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.94731772097293, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_system", + "name": "tool_list", + "status": "pass", + "message": "ok", + "duration_seconds": 44.292774281988386, + "agent_errors": [], + "details": { + "tool_calls": [ + "tool_list" + ], + "final_response": "Great! Here's a comprehensive summary of all **83 available tools** organized by category:\n\n## **Nostr Core Tools**\n1. **nostr_post** - Publish events (notes, reactions, longform)\n2. **nostr_query** -" + } + }, + { + "suite": "test_tools_system", + "name": "model_get", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.43525368801784, + "agent_errors": [], + "details": {} + }, + { + "suite": "test_tools_system", + "name": "model_list", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.39378822001163, + "agent_errors": [ + "[2026-04-04 10:26:46] [ERROR] [llm.c:93] [didactyl] llm http request failed: status=401" + ], + "details": {} + }, + { + "suite": "test_tools_system", + "name": "local_http_fetch", + "status": "pass", + "message": "ok", + "duration_seconds": 11.767529862001538, + "agent_errors": [], + "details": { + "tool_calls": [ + "local_http_fetch" + ], + "final_response": "Perfect! I've successfully fetched https://httpbin.org/get. Here's what was returned:\n\n**Response Details:**\n- **Status Code:** 200 (Success)\n- **Content Type:** application/json\n- **Response Body:**\n" + } + }, + { + "suite": "test_tools_system", + "name": "config_store_recall", + "status": "timeout", + "message": "timed out", + "duration_seconds": 73.9523419579491, + "agent_errors": [], + "details": {} + } + ], + "summary": { + "pass": 27, + "timeout": 16 + } +} \ No newline at end of file diff --git a/tests/results/20260404T140545Z/results.txt b/tests/results/20260404T140545Z/results.txt new file mode 100644 index 0000000..96be462 --- /dev/null +++ b/tests/results/20260404T140545Z/results.txt @@ -0,0 +1,140 @@ +== Didactyl Test Results == +Run: 2026-04-04T14:29:21.419470+00:00 +Agent base URL: http://127.0.0.1:8485 + +Pass: 27 +Fail: 0 +Error: 0 +Timeout: 16 +Skip: 0 +Total: 43 + +[PASS] test_conversation::simple_greeting (4.25s) + greeting response ok + +[PASS] test_conversation::agent_responds_about_itself (27.06s) + self description ok + +[PASS] test_conversation::empty_message_handling (4.67s) + empty message handled + +[TIMEOUT] test_conversation::very_long_message (76.51s) + timed out + +[PASS] test_errors::invalid_json_body (0.33s) + invalid json rejected + +[PASS] test_errors::missing_message_field (0.33s) + missing message rejected + +[PASS] test_errors::unknown_endpoint (0.33s) + unknown endpoint 404 + +[PASS] test_errors::webhook_nonexistent_dtag (0.33s) + nonexistent d_tag 404 + +[PASS] test_health::status_returns_200 (0.33s) + status success + +[PASS] test_health::status_has_fields (0.33s) + required fields present + +[PASS] test_health::context_current_returns_messages (0.33s) + context current ok + +[PASS] test_health::context_parts_has_system_prompt (0.33s) + context parts ok + +[PASS] test_restart::clean_restart (3.85s) + restart succeeded + +[PASS] test_restart::status_after_restart (4.15s) + status stable after restart + +[PASS] test_restart::conversation_after_restart (7.45s) + conversation works post-restart + +[PASS] test_timeouts::response_within_timeout (3.02s) + response within timeout + +[PASS] test_timeouts::status_responds_fast (0.33s) + status fast + +[PASS] test_timeouts::context_responds_fast (0.33s) + context fast + +[TIMEOUT] test_tools_blossom::blossom_list (73.46s) + timed out + +[PASS] test_tools_cashu::wallet_balance (7.05s) + ok + +[TIMEOUT] test_tools_identity::get_pubkey (73.43s) + timed out + +[PASS] test_tools_identity::get_npub (6.77s) + ok + +[TIMEOUT] test_tools_identity::agent_identity (73.39s) + timed out + +[PASS] test_tools_identity::agent_version (6.75s) + ok + +[TIMEOUT] test_tools_identity::admin_identity (74.42s) + timed out + +[PASS] test_tools_memory::task_list (8.36s) + ok + +[TIMEOUT] test_tools_memory::task_manage_add_remove (84.97s) + timed out + +[PASS] test_tools_memory::memory_save_recall (10.42s) + ok + +[TIMEOUT] test_tools_nostr::nostr_post_kind1 (73.97s) + timed out + +[PASS] test_tools_nostr::nostr_query_recent (14.23s) + ok + +[TIMEOUT] test_tools_nostr::nostr_my_events (74.02s) + timed out + +[PASS] test_tools_nostr::nostr_relay_status (6.10s) + ok + +[TIMEOUT] test_tools_nostr::nostr_dm_send (74.02s) + timed out + +[TIMEOUT] test_tools_nostr::nostr_encode_npub (73.42s) + timed out + +[TIMEOUT] test_tools_nostr::nostr_profile_get (74.39s) + timed out + +[PASS] test_tools_skills::skill_list (7.24s) + ok + +[TIMEOUT] test_tools_skills::trigger_list (73.41s) + timed out + +[TIMEOUT] test_tools_skills::skill_create_and_remove (73.95s) + timed out + +[PASS] test_tools_system::tool_list (44.29s) + ok + +[TIMEOUT] test_tools_system::model_get (73.44s) + timed out + +[TIMEOUT] test_tools_system::model_list (73.39s) + timed out + Agent errors: 1 + +[PASS] test_tools_system::local_http_fetch (11.77s) + ok + +[TIMEOUT] test_tools_system::config_store_recall (73.95s) + timed out diff --git a/tests/results/20260404T140545Z/runtime_test_genesis.jsonc b/tests/results/20260404T140545Z/runtime_test_genesis.jsonc new file mode 100644 index 0000000..8aa613f --- /dev/null +++ b/tests/results/20260404T140545Z/runtime_test_genesis.jsonc @@ -0,0 +1,53 @@ +{ + // TEST CONFIGURATION + // Use disposable keys/accounts only. + "key": { + "nsec": "d3011e59954403fb84729ab0e9fb239ba0f0238b3f353dfb5c08a32f4b3d21a1" + }, + "admin": { + "pubkey": "254d7a7f68b4443f5430564eb3a726f09aea57d27e75ab2b8ef57176f9ca3dac" + }, + "dm_protocol": "nip04", + "llm": { + "provider": "https://api.ppq.ai", + "api_key": "sk-SBI2Pga0J6n8jpPorHbRHC", + "model": "claude-haiku-4.5", + "base_url": "https://api.ppq.ai", + "max_tokens": 10000, + "temperature": 0.7 + }, + "api": { + "enabled": true, + "port": 8485, + "bind_address": "127.0.0.1" + }, + "startup_events": [ + { + "kind": 0, + "content_fields": { + "name": "Didactyl Test Agent", + "about": "Automated test instance" + }, + "tags": [] + }, + { + "kind": 10002, + "content": "", + "tags": [ + ["r", "wss://relay.damus.io"], + ["r", "wss://relay.primal.net"] + ] + }, + { + "kind": 31124, + "content": "# Test Agent\n\nYou are a test agent. Respond to requests and use tools when needed.", + "tags": [ + ["d", "identity_and_rules"], + ["app", "didactyl"], + ["scope", "private"], + ["trigger", "dm"], + ["filter", "{\"from\":\"admin\"}"] + ] + } + ] +}