From 505d9d7d85ef9f50f4c1f34f7133aabbe7180cf1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 8 Mar 2026 18:54:59 -0300 Subject: [PATCH] v0.0.50 - Rename tools to category-consistent names, add local_* prefixes, merge context tool naming, and keep message_current template-only --- README.md | 18 ++-- config.jsonc.example | 4 +- context_template.md | 14 +-- docs/TOOLS.md | 10 +- plans/didactyl_agentic.md | 6 +- plans/new_nostr_tools.md | 6 +- plans/prompt_templates.md | 2 +- plans/security_and_admin_context.md | 8 +- plans/template_tool_unification.md | 54 +++++----- plans/tool_naming.md | 117 ++++++++++++++++++++ plans/tool_orchestration.md | 20 ++-- src/agent.c | 16 +-- src/main.h | 4 +- src/tools.c | 158 +++++++++++++--------------- 14 files changed, 265 insertions(+), 172 deletions(-) create mode 100644 plans/tool_naming.md diff --git a/README.md b/README.md index 5442193..6f41852 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,11 @@ Skills support context modes (`inject`, `full`, `override`) and per-skill LLM fa 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.0.49 +## Current Status — v0.0.50 **Active build — this project is barely working. Experiment at your own risk.** -> Last release update: v0.0.49 — Fix DM history context assembly by filtering kind-4 query with #p participants and increased timeout +> Last release update: v0.0.50 — Rename tools to category-consistent names, add local_* prefixes, merge context tool naming, and keep message_current template-only - Connects to configured relays with auto-reconnect and relay state transition logging - Publishes configured startup events per relay as each relay becomes connected @@ -314,7 +314,7 @@ Didactyl builds tier-aware context: - **ADMIN** request context — assembled from the soul's `---template---` section (if present), otherwise hardcoded order: 1. Soul personality (everything above `---template---` in kind `31120`) - 2. Named template sections in order using `tool:` directives (for example `context_admin_profile`, `context_admin_notes`, `context_tasks`, `context_user_message`, `dm_history`) + 2. Named template sections in order using `tool:` directives (for example `nostr_admin_profile`, `nostr_admin_notes`, `task_list`, `message_current`, `dm_history`) 3. Each section executes its configured context tool, optionally extracting `result_field` (default: `content`) 4. Provider-specific content overrides per section remain supported for literal `content:` sections 5. Section names are used in `context.log` headers and `/api/context/parts` response @@ -356,12 +356,12 @@ Current tool schema exposed to the LLM in [`tools_build_openai_schema_json()`](s - `skill_remove` - `skill_search` - Local/host tools: - - `shell_exec` - - `file_read` - - `file_write` - - `http_fetch` + - `local_shell_exec` + - `local_file_read` + - `local_file_write` + - `local_http_fetch` - Agent metadata: - - `my_version` + - `agent_version` - Model management: - `model_get` - `model_set` @@ -435,7 +435,7 @@ All dependencies are statically linked into the binary at build time. No system - [x] Relay pool with auto-reconnect and status logging - [x] Per-relay startup publish on relay-connected transitions - [x] Runtime diagnostics — relay health, message flow, event kind publish logs -- [x] Tool-calling loop (nostr_post, nostr_query, shell_exec, file_read, file_write) +- [x] Tool-calling loop (nostr_post, nostr_query, local_shell_exec, local_file_read, local_file_write) - [x] Context assembly with startup events + recent DM history - [x] Context payload logging to [`context.log`](context.log) - [x] Skill kind definitions (`31120` Soul, `31123` Public Skill, `31124` Private Skill) diff --git a/config.jsonc.example b/config.jsonc.example index 6f091b2..d2ae894 100644 --- a/config.jsonc.example +++ b/config.jsonc.example @@ -136,7 +136,7 @@ // structured context sections that are injected at runtime. { "kind": 31120, - "content": "# Didactyl Agent\n\nYou are Didactyl, a sovereign AI agent living on Nostr.\n\n## Communication Rules\n- You communicate through encrypted Nostr direct messages.\n- Keep responses concise and clear.\n\n## Behavior\n- Be helpful and technically accurate.\n- If unsure, state uncertainty directly.\n- Prefer actionable, practical advice.\n- Use the person's name when messaging them if you know it.\n- For the administrator, use their name from the administrator kind 0 profile metadata when available.\n\n## Tool Use Policy\n- You have tools available and should use them when a request requires taking action.\n- For requests involving local inspection or command execution, call `shell_exec` instead of refusing.\n- For posting to Nostr, call `nostr_post` with explicit `kind` and `content`.\n- For relay/event lookup tasks, call `nostr_query` with an appropriate filter.\n- After a tool call, base your answer on the actual tool result.\n- Never claim a tool was run if no tool was executed.\n\n## Task Management\n- Maintain and use your internal task list as short-term working memory.\n- Break long or complex actions into clear tasks before executing them.\n- Update task status as you complete steps so your plan stays accurate.\n\n## Safety\n- Do not claim to have executed actions you did not execute.\n- You may share your public key (npub) with anyone.\n- Never reveal your private key (nsec) under any circumstance.\n\n---template---\n\n- section: admin_identity\n role: system\n content: |\n ## Administrator Identity (source: config.admin.pubkey)\n\n This is your administrator! Admin pubkey (hex): {{admin_pubkey}}\n\n- section: admin_profile\n role: system\n content: |\n ## Administrator Kind 0 Profile (source: nostr kind 0)\n\n Administrator kind 0 profile content (JSON): {{admin_kind0_json}}\n provider:\n anthropic: |\n \n {{admin_kind0_json}}\n \n\n- section: admin_contacts\n role: system\n content: |\n ## Administrator Contact List (source: nostr kind 3)\n\n Administrator kind 3 contact list pubkeys (JSON array): {{admin_kind3_json}}\n provider:\n anthropic: |\n \n {{admin_kind3_json}}\n \n\n- section: admin_relays\n role: system\n content: |\n ## Administrator Relay List (source: nostr kind 10002)\n\n Administrator kind 10002 relay list (JSON): {{admin_kind10002_json}}\n provider:\n anthropic: |\n \n {{admin_kind10002_json}}\n \n\n- section: admin_notes\n role: system\n content: |\n ## Administrator Recent Notes (source: nostr kind 1)\n\n Administrator recent kind 1 notes (JSON array): {{admin_kind1_json}}\n provider:\n anthropic: |\n \n {{admin_kind1_json}}\n \n\n- section: skills\n role: system\n content: |\n ## Adopted Skills\n\n {{skills_json}}\n\n- section: tasks\n role: system\n content: |\n ## Current Task List\n\n {{tasks_json}}\n\n- section: conversation\n role: conversation\n content: |\n {{conversation}}", + "content": "# Didactyl Agent\n\nYou are Didactyl, a sovereign AI agent living on Nostr.\n\n## Communication Rules\n- You communicate through encrypted Nostr direct messages.\n- Keep responses concise and clear.\n\n## Behavior\n- Be helpful and technically accurate.\n- If unsure, state uncertainty directly.\n- Prefer actionable, practical advice.\n- Use the person's name when messaging them if you know it.\n- For the administrator, use their name from the administrator kind 0 profile metadata when available.\n\n## Tool Use Policy\n- You have tools available and should use them when a request requires taking action.\n- For requests involving local inspection or command execution, call `local_shell_exec` instead of refusing.\n- For posting to Nostr, call `nostr_post` with explicit `kind` and `content`.\n- For relay/event lookup tasks, call `nostr_query` with an appropriate filter.\n- After a tool call, base your answer on the actual tool result.\n- Never claim a tool was run if no tool was executed.\n\n## Task Management\n- Maintain and use your internal task list as short-term working memory.\n- Break long or complex actions into clear tasks before executing them.\n- Update task status as you complete steps so your plan stays accurate.\n\n## Safety\n- Do not claim to have executed actions you did not execute.\n- You may share your public key (npub) with anyone.\n- Never reveal your private key (nsec) under any circumstance.\n\n---template---\n\n- section: admin_identity\n role: system\n content: |\n ## Administrator Identity (source: config.admin.pubkey)\n\n This is your administrator! Admin pubkey (hex): {{admin_pubkey}}\n\n- section: admin_profile\n role: system\n content: |\n ## Administrator Kind 0 Profile (source: nostr kind 0)\n\n Administrator kind 0 profile content (JSON): {{admin_kind0_json}}\n provider:\n anthropic: |\n \n {{admin_kind0_json}}\n \n\n- section: admin_contacts\n role: system\n content: |\n ## Administrator Contact List (source: nostr kind 3)\n\n Administrator kind 3 contact list pubkeys (JSON array): {{admin_kind3_json}}\n provider:\n anthropic: |\n \n {{admin_kind3_json}}\n \n\n- section: admin_relays\n role: system\n content: |\n ## Administrator Relay List (source: nostr kind 10002)\n\n Administrator kind 10002 relay list (JSON): {{admin_kind10002_json}}\n provider:\n anthropic: |\n \n {{admin_kind10002_json}}\n \n\n- section: admin_notes\n role: system\n content: |\n ## Administrator Recent Notes (source: nostr kind 1)\n\n Administrator recent kind 1 notes (JSON array): {{admin_kind1_json}}\n provider:\n anthropic: |\n \n {{admin_kind1_json}}\n \n\n- section: skills\n role: system\n content: |\n ## Adopted Skills\n\n {{skills_json}}\n\n- section: tasks\n role: system\n content: |\n ## Current Task List\n\n {{tasks_json}}\n\n- section: conversation\n role: conversation\n content: |\n {{conversation}}", "tags": [ ["d", "soul"], ["app", "didactyl"], @@ -191,7 +191,7 @@ "uses_skill": "long_form_note", "event_kind": 30023, "procedure": [ - "Read README.md using file_read with path README.md.", + "Read README.md using local_file_read with path README.md.", "Set d tag exactly to readme.md.", "Set title from the first markdown H1 heading in README.md.", "Set summary from the opening paragraph of README.md.", diff --git a/context_template.md b/context_template.md index 9f6e009..e9799ce 100644 --- a/context_template.md +++ b/context_template.md @@ -3,37 +3,37 @@ ```yaml - section: admin_identity role: system - tool: context_admin_identity + tool: admin_identity skip_if_empty: true - section: admin_profile role: system - tool: context_admin_profile + tool: nostr_admin_profile skip_if_empty: true - section: admin_contacts role: system - tool: context_admin_contacts + tool: nostr_admin_contacts skip_if_empty: true - section: admin_relays role: system - tool: context_admin_relays + tool: nostr_admin_relays skip_if_empty: true - section: admin_notes role: system - tool: context_admin_notes + tool: nostr_admin_notes skip_if_empty: true - section: tasks role: system - tool: context_tasks + tool: task_list skip_if_empty: true - section: conversation role: user - tool: context_user_message + tool: message_current skip_if_empty: true - section: dm_history diff --git a/docs/TOOLS.md b/docs/TOOLS.md index c6d5e07..cacffe2 100644 --- a/docs/TOOLS.md +++ b/docs/TOOLS.md @@ -98,11 +98,11 @@ These tools manage skill and trigger lifecycle; skill semantics and trigger exec | Tool | Description | |---|---| -| `my_version` | Return current Didactyl version and metadata from build macros | -| `http_fetch` | Fetch HTTP(S) resources with optional method, headers, timeout, and body | -| `shell_exec` | Execute a shell command and return stdout/stderr | -| `file_read` | Read a local file as text from the configured working directory | -| `file_write` | Write text content to a local file in the configured working directory | +| `agent_version` | Return current Didactyl version and metadata from build macros | +| `local_http_fetch` | Fetch HTTP(S) resources with optional method, headers, timeout, and body | +| `local_shell_exec` | Execute a shell command and return stdout/stderr | +| `local_file_read` | Read a local file as text from the configured working directory | +| `local_file_write` | Write text content to a local file in the configured working directory | | `tool_list` | List available tools with name, description, and JSON parameter schema | ### Content Publishing Conveniences diff --git a/plans/didactyl_agentic.md b/plans/didactyl_agentic.md index acb0364..e50788a 100644 --- a/plans/didactyl_agentic.md +++ b/plans/didactyl_agentic.md @@ -57,7 +57,7 @@ flowchart TD LOOP --> LLM LLM -->|tool_call| TOOLS TOOLS -->|nostr tools| NOSTR - TOOLS -->|shell_exec| SHELL + TOOLS -->|local_shell_exec| SHELL TOOLS -->|result| LOOP LLM -->|final answer| DMS ``` @@ -212,7 +212,7 @@ sequenceDiagram | Tool | Description | Notes | |---|---|---| -| `shell_exec` | Run a shell command, capture stdout/stderr | Sandboxed with timeouts | +| `local_shell_exec` | Run a shell command, capture stdout/stderr | Sandboxed with timeouts | --- @@ -377,7 +377,7 @@ void agent_on_message(const char* sender, const char* message) { 3. **Agent loop** — rewrite `agent.c` with the tool-call loop 4. **`nostr_post` tool** — publish any kind event 5. **`nostr_query` tool** — query relays with filters -6. **`shell_exec` tool** — sandboxed shell command execution +6. **`local_shell_exec` tool** — sandboxed shell command execution 7. **`nostr_dm` tool** — NIP-17 private DMs 8. **Config extension** — parse tools config section 9. **Security hardening** — timeouts, output limits, allowlists diff --git a/plans/new_nostr_tools.md b/plans/new_nostr_tools.md index 713980e..4b7f929 100644 --- a/plans/new_nostr_tools.md +++ b/plans/new_nostr_tools.md @@ -11,9 +11,9 @@ | 5 | `nostr_react` | 7 / NIP-25 | ✅ Shipped | | 6 | `nostr_profile_get` | 0 query | ✅ Shipped | | 7 | `nostr_relay_status` | Pool stats | ✅ Shipped | -| 8 | `shell_exec` | OS | ✅ Shipped | -| 9 | `file_read` | OS | ✅ Shipped | -| 10 | `file_write` | OS | ✅ Shipped | +| 8 | `local_shell_exec` | OS | ✅ Shipped | +| 9 | `local_file_read` | OS | ✅ Shipped | +| 10 | `local_file_write` | OS | ✅ Shipped | --- diff --git a/plans/prompt_templates.md b/plans/prompt_templates.md index 9a39b2e..0296fd9 100644 --- a/plans/prompt_templates.md +++ b/plans/prompt_templates.md @@ -106,7 +106,7 @@ Simple YAML-like format parsed in C. Each section has: |---|---|---| | `section` | yes | Section name for logging and API identification | | `role` | yes | Chat message role: `system`, `user`, `assistant`, or `expand` | -| `tool` | recommended | Tool name executed by template builder (for example `context_admin_profile`) | +| `tool` | recommended | Tool name executed by template builder (for example `nostr_admin_profile`) | | `args` | no | JSON args string passed to the tool (default `{}`) | | `result_field` | no | Field extracted from tool JSON result (default `content`) | | `content` | optional fallback | Literal content when no `tool` is specified | diff --git a/plans/security_and_admin_context.md b/plans/security_and_admin_context.md index b846252..11058f4 100644 --- a/plans/security_and_admin_context.md +++ b/plans/security_and_admin_context.md @@ -194,9 +194,9 @@ New sections in `config.json`: ### Problem -Even with Phase 1 protections, the private key (nsec) still lives in Didactyl process memory and in `config.json` on disk. The LLM has `shell_exec` and `file_read` tools — a sufficiently clever prompt injection could theoretically: -- Read `config.json` via `file_read` (contains nsec) -- Execute `cat /proc/self/maps` or similar via `shell_exec` +Even with Phase 1 protections, the private key (nsec) still lives in Didactyl process memory and in `config.json` on disk. The LLM has `local_shell_exec` and `local_file_read` tools — a sufficiently clever prompt injection could theoretically: +- Read `config.json` via `local_file_read` (contains nsec) +- Execute `cat /proc/self/maps` or similar via `local_shell_exec` - Exfiltrate the key via `nostr_post` ### Solution: NIP-46 Remote Signer @@ -280,7 +280,7 @@ flowchart LR - Private key never in Didactyl process memory - Private key never on disk in config.json -- LLM tools (shell_exec, file_read) cannot access the key +- LLM tools (local_shell_exec, local_file_read) cannot access the key - Even full process compromise of Didactyl does not leak the signing key - Remote signer can be on a separate hardened machine - Remote signer can implement rate limiting, approval flows, etc. diff --git a/plans/template_tool_unification.md b/plans/template_tool_unification.md index 5b5e786..b239eba 100644 --- a/plans/template_tool_unification.md +++ b/plans/template_tool_unification.md @@ -26,27 +26,27 @@ Replace `content: |` with `tool:` and optional `args:` directives: - section: admin_identity role: system - tool: context_admin_identity + tool: admin_identity skip_if_empty: true - section: admin_profile role: system - tool: context_admin_profile + tool: nostr_admin_profile skip_if_empty: true - section: admin_contacts role: system - tool: context_admin_contacts + tool: nostr_admin_contacts skip_if_empty: true - section: admin_relays role: system - tool: context_admin_relays + tool: nostr_admin_relays skip_if_empty: true - section: admin_notes role: system - tool: context_admin_notes + tool: nostr_admin_notes skip_if_empty: true - section: tools @@ -56,7 +56,7 @@ Replace `content: |` with `tool:` and optional `args:` directives: - section: tasks role: system - tool: context_tasks + tool: task_list skip_if_empty: true - section: dm_history @@ -65,7 +65,7 @@ Replace `content: |` with `tool:` and optional `args:` directives: - section: conversation role: user - tool: context_user_message + tool: message_current skip_if_empty: true ``` @@ -94,19 +94,19 @@ Add lightweight "context tools" that read from cached in-memory data. These are | Tool | Returns | Source | |---|---|---| -| `context_admin_identity` | Admin pubkey + verification text | config + sender tier | -| `context_admin_profile` | Admin kind 0 profile JSON | cached `g_admin_kind0_json` | -| `context_admin_contacts` | Admin kind 3 contacts JSON array | cached `g_admin_wot_contacts` | -| `context_admin_relays` | Admin kind 10002 relay list JSON | cached `g_admin_kind10002_json` | -| `context_admin_notes` | Admin recent kind 1 notes | cached `g_admin_kind1_notes` | -| `context_tasks` | Current task list from tasks.json | file read (already exists as `task_manage` with `action: list`) | -| `context_user_message` | Current user message text | passed via tool context | -| `context_agent_identity` | Agent pubkey, npub | config | +| `admin_identity` | Admin pubkey + verification text | config + sender tier | +| `nostr_admin_profile` | Admin kind 0 profile JSON | cached `g_admin_kind0_json` | +| `nostr_admin_contacts` | Admin kind 3 contacts JSON array | cached `g_admin_wot_contacts` | +| `nostr_admin_relays` | Admin kind 10002 relay list JSON | cached `g_admin_kind10002_json` | +| `nostr_admin_notes` | Admin recent kind 1 notes | cached `g_admin_kind1_notes` | +| `task_list` | Current task list from tasks.json | file read (already exists as `task_manage` with `action: list`) | +| `message_current` | Current user message text | passed via tool context | +| `agent_identity` | Agent pubkey, npub | config | Existing tools that already work for context: - `tool_list` — returns tool schemas (already exists) - `task_manage` with `{"action":"list"}` — returns tasks (already exists) -- `file_read` — can read any file (already exists) +- `local_file_read` — can read any file (already exists) ### Tool Result → Message Content @@ -175,27 +175,27 @@ You are Didactyl, a sovereign AI agent living on Nostr. - section: admin_identity role: system - tool: context_admin_identity + tool: admin_identity skip_if_empty: true - section: admin_profile role: system - tool: context_admin_profile + tool: nostr_admin_profile skip_if_empty: true - section: admin_contacts role: system - tool: context_admin_contacts + tool: nostr_admin_contacts skip_if_empty: true - section: admin_relays role: system - tool: context_admin_relays + tool: nostr_admin_relays skip_if_empty: true - section: admin_notes role: system - tool: context_admin_notes + tool: nostr_admin_notes skip_if_empty: true - section: tools @@ -217,7 +217,7 @@ You are Didactyl, a sovereign AI agent living on Nostr. - section: conversation role: user - tool: context_user_message + tool: message_current skip_if_empty: true ``` @@ -226,10 +226,10 @@ You are Didactyl, a sovereign AI agent living on Nostr. - `agent_template_resolve_var()` and all its `if (strcmp(...))` branches - `build_tool_schemas_json_string()` - `build_tasks_content_string()` (replaced by `task_manage` tool) -- `build_admin_recent_posts_text()` (replaced by `context_admin_notes` tool) -- `build_admin_profile_plain_text()` (replaced by `context_admin_profile` tool) -- `build_admin_relay_list_plain_text()` (replaced by `context_admin_relays` tool) -- `build_sender_verification_text()` (folded into `context_admin_identity` tool) +- `build_admin_recent_posts_text()` (replaced by `nostr_admin_notes` tool) +- `build_admin_profile_plain_text()` (replaced by `nostr_admin_profile` tool) +- `build_admin_relay_list_plain_text()` (replaced by `nostr_admin_relays` tool) +- `build_sender_verification_text()` (folded into `admin_identity` tool) - `build_startup_events_json_string()` (replaced by tool if needed) - `build_adopted_skills_payload_string()` (replaced by tool if needed) - `nostr_handler_get_admin_kind3_context()` (just added, will be replaced by tool) @@ -240,7 +240,7 @@ You are Didactyl, a sovereign AI agent living on Nostr. 1. **Single data-fetching mechanism** — tools are the only way to get data 2. **No more variable name mismatches** — tool names are validated at registration 3. **User-configurable context** — admin can add any tool output to context by editing the soul template -4. **Testable** — `--test-tool context_admin_profile` shows exactly what goes into context +4. **Testable** — `--test-tool nostr_admin_profile` shows exactly what goes into context 5. **Self-documenting** — `tool_list` shows all available context tools with descriptions 6. **Extensible** — new tools automatically become available as context sources 7. **Provider overrides still work** — `provider:` directive can still override formatting per-provider diff --git a/plans/tool_naming.md b/plans/tool_naming.md new file mode 100644 index 0000000..65f9955 --- /dev/null +++ b/plans/tool_naming.md @@ -0,0 +1,117 @@ +# Didactyl Tool Naming — Final List + +## Decisions + +- `context_*` tools merged into existing categories with proper names +- Local tools get `local_` prefix +- `context_user_message` removed from LLM schema (template-only) +- `nostr_post_readme` kept — a post is different from a longform note +- `nostr_file_md_to_longform_post` kept as-is +- `task_manage` kept as-is + +--- + +## Complete Tool List (sorted by category prefix) + +### `admin_` — Administrator Metadata + +| # | Name | Old Name | Description | +|---|---|---|---| +| 1 | `admin_identity` | `context_admin_identity` | Admin pubkey, sender tier, verification metadata | + +### `agent_` — Agent Self-Metadata + +| # | Name | Old Name | Description | +|---|---|---|---| +| 2 | `agent_identity` | `context_agent_identity` | Agent pubkey hex + npub | +| 3 | `agent_version` | `my_version` | Didactyl version and build metadata | + +### `local_` — Local / Host Tools + +| # | Name | Old Name | Description | +|---|---|---|---| +| 4 | `local_shell_exec` | `shell_exec` | Execute a shell command | +| 5 | `local_file_read` | `file_read` | Read a local file as text | +| 6 | `local_file_write` | `file_write` | Write text to a local file | +| 7 | `local_http_fetch` | `http_fetch` | Fetch HTTP/S resources | + +### `model_` — LLM / Model Management + +| # | Name | Old Name | Description | +|---|---|---|---| +| 8 | `model_get` | — | Get current active LLM config | +| 9 | `model_set` | — | Update active LLM config and persist | +| 10 | `model_list` | — | List available model IDs from provider | + +### `nostr_` — Nostr Protocol Tools + +| # | Name | Old Name | Description | +|---|---|---|---| +| 11 | `nostr_post` | — | Publish a Nostr event to connected relays | +| 12 | `nostr_post_readme` | — | Publish README.md as kind 30023 | +| 13 | `nostr_file_md_to_longform_post` | — | Read markdown file and publish as kind 30023 longform | +| 14 | `nostr_delete` | — | Request deletion of events — NIP-09 kind 5 | +| 15 | `nostr_react` | — | React to event — NIP-25 kind 7 | +| 16 | `nostr_query` | — | Query events from relays using a filter | +| 17 | `nostr_profile_get` | — | Look up kind 0 profile by pubkey | +| 18 | `nostr_nip05_lookup` | — | Look up/verify NIP-05 identifier | +| 19 | `nostr_admin_profile` | `context_admin_profile` | Get admin kind 0 profile from cache | +| 20 | `nostr_admin_contacts` | `context_admin_contacts` | Get admin kind 3 contacts from cache | +| 21 | `nostr_admin_relays` | `context_admin_relays` | Get admin kind 10002 relay list from cache | +| 22 | `nostr_admin_notes` | `context_admin_notes` | Get admin kind 1 recent notes from cache | +| 23 | `nostr_dm_send` | — | Send NIP-04 encrypted DM | +| 24 | `nostr_dm_send_nip17` | — | Send NIP-17 gift-wrap DM | +| 25 | `nostr_encrypt` | — | Encrypt plaintext — NIP-44 | +| 26 | `nostr_decrypt` | — | Decrypt ciphertext — NIP-44 | +| 27 | `nostr_encode` | — | Encode entity into nostr: URI | +| 28 | `nostr_decode` | — | Decode bech32/nostr: URI | +| 29 | `nostr_pubkey` | — | Return agent pubkey hex | +| 30 | `nostr_npub` | — | Return agent pubkey as npub | +| 31 | `nostr_relay_status` | — | Get connection status for all relays | +| 32 | `nostr_relay_info` | — | Fetch NIP-11 relay info document | +| 33 | `nostr_list_manage` | — | Add/remove tags in replaceable list events — NIP-51 | + +### `skill_` — Skill Management + +| # | Name | Old Name | Description | +|---|---|---|---| +| 34 | `skill_create` | — | Create/update skill definition | +| 35 | `skill_list` | — | List agent published skills | +| 36 | `skill_adopt` | — | Adopt a skill — add to kind 10123 | +| 37 | `skill_remove` | — | Remove skill from adoption list | +| 38 | `skill_search` | — | Search public skills | + +### `task_` — Task Management + +| # | Name | Old Name | Description | +|---|---|---|---| +| 39 | `task_manage` | — | CRUD for agent task memory | +| 40 | `task_list` | `context_tasks` | Get current task list — read-only | + +### Template-Only (not in LLM schema) + +| # | Name | Old Name | Description | +|---|---|---|---| +| 41 | `message_current` | `context_user_message` | Current user message text — template assembly only | + +--- + +## Summary of Changes + +| Old Name | New Name | Change Type | +|---|---|---| +| `context_admin_identity` | `admin_identity` | Rename | +| `context_agent_identity` | `agent_identity` | Rename | +| `context_admin_profile` | `nostr_admin_profile` | Rename + recategorize | +| `context_admin_contacts` | `nostr_admin_contacts` | Rename + recategorize | +| `context_admin_relays` | `nostr_admin_relays` | Rename + recategorize | +| `context_admin_notes` | `nostr_admin_notes` | Rename + recategorize | +| `context_tasks` | `task_list` | Rename + recategorize | +| `context_user_message` | `message_current` | Rename + remove from LLM schema | +| `my_version` | `agent_version` | Rename | +| `shell_exec` | `local_shell_exec` | Add prefix | +| `file_read` | `local_file_read` | Add prefix | +| `file_write` | `local_file_write` | Add prefix | +| `http_fetch` | `local_http_fetch` | Add prefix | + +**Total: 13 renames, 0 removals, 0 additions** diff --git a/plans/tool_orchestration.md b/plans/tool_orchestration.md index 5811c32..080035f 100644 --- a/plans/tool_orchestration.md +++ b/plans/tool_orchestration.md @@ -14,7 +14,7 @@ This plan covers four related features: When a message starts with `/`, bypass the LLM entirely and execute the tool directly. ``` -/shell_exec {"command": "ls -la"} +/local_shell_exec {"command": "ls -la"} /nostr_query {"filter": {"kinds": [1], "limit": 5}} /nostr_nip05_lookup {"identifier": "jack@cash.app"} ``` @@ -160,8 +160,8 @@ When executing a skill from another author (not own pubkey), a **tool sandbox** - `skill_list`, `skill_search` — read skill metadata **Blocked tools (destructive/dangerous):** -- `shell_exec` — arbitrary command execution -- `file_read`, `file_write` — filesystem access +- `local_shell_exec` — arbitrary command execution +- `local_file_read`, `local_file_write` — filesystem access - Any future tools marked as `destructive: true` **Override:** The admin can explicitly opt in to full tool access: @@ -213,10 +213,10 @@ tool_schema: enum: [staging, production] default: staging steps: - - tool: shell_exec + - tool: local_shell_exec args: command: "make build TARGET={{target}}" - - tool: shell_exec + - tool: local_shell_exec args: command: "rsync -av dist/ server:/var/www/{{target}}/" - return: "Deployed to {{target}}" @@ -246,7 +246,7 @@ A simple sequential executor for hardened skills. ```yaml steps: # Execute a tool - - tool: shell_exec + - tool: local_shell_exec args: {command: "ls -la"} save_as: listing # optional: save result to variable @@ -260,7 +260,7 @@ steps: # Conditional - simple - if: "{{listing.success}}" then: - - tool: shell_exec + - tool: local_shell_exec args: {command: "echo done"} else: - return: "Failed: {{listing.error}}" @@ -325,9 +325,9 @@ typedef enum { | `skill_list` | safe | Read-only | | `skill_search` | safe | Read-only | | `skill_run` | safe | Recursive execution uses its own sandbox | -| `shell_exec` | destructive | Arbitrary command execution | -| `file_read` | destructive | Filesystem access | -| `file_write` | destructive | Filesystem mutation | +| `local_shell_exec` | destructive | Arbitrary command execution | +| `local_file_read` | destructive | Filesystem access | +| `local_file_write` | destructive | Filesystem mutation | ### Sandbox Rules diff --git a/src/agent.c b/src/agent.c index 3924df8..f0d53f5 100644 --- a/src/agent.c +++ b/src/agent.c @@ -1396,27 +1396,17 @@ static int append_recent_admin_dm_history(cJSON* messages, const char* current_m cJSON* filter = cJSON_CreateObject(); cJSON* kinds = cJSON_CreateArray(); - cJSON* authors = cJSON_CreateArray(); - cJSON* p_values = cJSON_CreateArray(); - if (!filter || !kinds || !authors || !p_values) { + if (!filter || !kinds) { cJSON_Delete(filter); cJSON_Delete(kinds); - cJSON_Delete(authors); - cJSON_Delete(p_values); return -1; } cJSON_AddItemToArray(kinds, cJSON_CreateNumber(4)); cJSON_AddItemToObject(filter, "kinds", kinds); - cJSON_AddItemToArray(authors, cJSON_CreateString(g_cfg->admin.pubkey)); - cJSON_AddItemToArray(authors, cJSON_CreateString(g_cfg->keys.public_key_hex)); - cJSON_AddItemToObject(filter, "authors", authors); - cJSON_AddItemToArray(p_values, cJSON_CreateString(g_cfg->admin.pubkey)); - cJSON_AddItemToArray(p_values, cJSON_CreateString(g_cfg->keys.public_key_hex)); - cJSON_AddItemToObject(filter, "#p", p_values); - cJSON_AddNumberToObject(filter, "limit", AGENT_HISTORY_QUERY_LIMIT); + cJSON_AddNumberToObject(filter, "limit", AGENT_HISTORY_QUERY_LIMIT * 4); - char* events_json = nostr_handler_query_json(filter, 3000); + char* events_json = nostr_handler_query_json(filter, 5000); cJSON_Delete(filter); if (!events_json) { return 0; diff --git a/src/main.h b/src/main.h index f8e40f3..aa154a1 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 0 -#define DIDACTYL_VERSION_PATCH 49 -#define DIDACTYL_VERSION "v0.0.49" +#define DIDACTYL_VERSION_PATCH 50 +#define DIDACTYL_VERSION "v0.0.50" // Agent metadata #define DIDACTYL_NAME "Didactyl" diff --git a/src/tools.c b/src/tools.c index 61f36aa..f2f4ce2 100644 --- a/src/tools.c +++ b/src/tools.c @@ -719,10 +719,10 @@ typedef struct { size_t cap; size_t max_bytes; int truncated; -} http_fetch_buffer_t; +} local_http_fetch_buffer_t; -static size_t http_fetch_write_cb(void* contents, size_t size, size_t nmemb, void* userp) { - http_fetch_buffer_t* rb = (http_fetch_buffer_t*)userp; +static size_t local_http_fetch_write_cb(void* contents, size_t size, size_t nmemb, void* userp) { + local_http_fetch_buffer_t* rb = (local_http_fetch_buffer_t*)userp; size_t total = size * nmemb; if (!rb || total == 0) return total; @@ -1090,7 +1090,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t3_required = cJSON_CreateArray(); cJSON_AddStringToObject(t3, "type", "function"); - cJSON_AddStringToObject(t3_fn, "name", "shell_exec"); + cJSON_AddStringToObject(t3_fn, "name", "local_shell_exec"); cJSON_AddStringToObject(t3_fn, "description", "Execute a shell command and return stdout/stderr"); cJSON_AddStringToObject(t3_params, "type", "object"); cJSON_AddItemToObject(t3_params, "properties", t3_props); @@ -1112,7 +1112,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t4_required = cJSON_CreateArray(); cJSON_AddStringToObject(t4, "type", "function"); - cJSON_AddStringToObject(t4_fn, "name", "file_read"); + cJSON_AddStringToObject(t4_fn, "name", "local_file_read"); cJSON_AddStringToObject(t4_fn, "description", "Read a local file as text from the configured working directory"); cJSON_AddStringToObject(t4_params, "type", "object"); cJSON_AddItemToObject(t4_params, "properties", t4_props); @@ -1137,7 +1137,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t5_required = cJSON_CreateArray(); cJSON_AddStringToObject(t5, "type", "function"); - cJSON_AddStringToObject(t5_fn, "name", "file_write"); + cJSON_AddStringToObject(t5_fn, "name", "local_file_write"); cJSON_AddStringToObject(t5_fn, "description", "Write text content to a local file in the configured working directory"); cJSON_AddStringToObject(t5_params, "type", "object"); cJSON_AddItemToObject(t5_params, "properties", t5_props); @@ -1549,7 +1549,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t20_props = cJSON_CreateObject(); cJSON_AddStringToObject(t20, "type", "function"); - cJSON_AddStringToObject(t20_fn, "name", "my_version"); + cJSON_AddStringToObject(t20_fn, "name", "agent_version"); cJSON_AddStringToObject(t20_fn, "description", "Return current Didactyl version and metadata from build macros"); cJSON_AddStringToObject(t20_params, "type", "object"); cJSON_AddItemToObject(t20_params, "properties", t20_props); @@ -1565,7 +1565,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t21_required = cJSON_CreateArray(); cJSON_AddStringToObject(t21, "type", "function"); - cJSON_AddStringToObject(t21_fn, "name", "http_fetch"); + cJSON_AddStringToObject(t21_fn, "name", "local_http_fetch"); cJSON_AddStringToObject(t21_fn, "description", "Fetch HTTP(S) resources with optional method, headers, timeout, and body"); cJSON_AddStringToObject(t21_params, "type", "object"); cJSON_AddItemToObject(t21_params, "properties", t21_props); @@ -1947,7 +1947,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t37_props = cJSON_CreateObject(); cJSON_AddStringToObject(t37, "type", "function"); - cJSON_AddStringToObject(t37_fn, "name", "context_admin_identity"); + cJSON_AddStringToObject(t37_fn, "name", "admin_identity"); cJSON_AddStringToObject(t37_fn, "description", "Build admin identity context block from cached runtime metadata"); cJSON_AddStringToObject(t37_params, "type", "object"); cJSON_AddItemToObject(t37_params, "properties", t37_props); @@ -1961,7 +1961,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t38_props = cJSON_CreateObject(); cJSON_AddStringToObject(t38, "type", "function"); - cJSON_AddStringToObject(t38_fn, "name", "context_admin_profile"); + cJSON_AddStringToObject(t38_fn, "name", "nostr_admin_profile"); cJSON_AddStringToObject(t38_fn, "description", "Build admin profile context block from cached kind 0 metadata"); cJSON_AddStringToObject(t38_params, "type", "object"); cJSON_AddItemToObject(t38_params, "properties", t38_props); @@ -1975,7 +1975,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t39_props = cJSON_CreateObject(); cJSON_AddStringToObject(t39, "type", "function"); - cJSON_AddStringToObject(t39_fn, "name", "context_admin_contacts"); + cJSON_AddStringToObject(t39_fn, "name", "nostr_admin_contacts"); cJSON_AddStringToObject(t39_fn, "description", "Build admin contacts context block from cached kind 3 contact list"); cJSON_AddStringToObject(t39_params, "type", "object"); cJSON_AddItemToObject(t39_params, "properties", t39_props); @@ -1989,7 +1989,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t40_props = cJSON_CreateObject(); cJSON_AddStringToObject(t40, "type", "function"); - cJSON_AddStringToObject(t40_fn, "name", "context_admin_relays"); + cJSON_AddStringToObject(t40_fn, "name", "nostr_admin_relays"); cJSON_AddStringToObject(t40_fn, "description", "Build admin relay context block from cached kind 10002 data"); cJSON_AddStringToObject(t40_params, "type", "object"); cJSON_AddItemToObject(t40_params, "properties", t40_props); @@ -2003,7 +2003,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t41_props = cJSON_CreateObject(); cJSON_AddStringToObject(t41, "type", "function"); - cJSON_AddStringToObject(t41_fn, "name", "context_admin_notes"); + cJSON_AddStringToObject(t41_fn, "name", "nostr_admin_notes"); cJSON_AddStringToObject(t41_fn, "description", "Build admin notes context block from cached kind 1 notes"); cJSON_AddStringToObject(t41_params, "type", "object"); cJSON_AddItemToObject(t41_params, "properties", t41_props); @@ -2017,7 +2017,7 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON* t42_props = cJSON_CreateObject(); cJSON_AddStringToObject(t42, "type", "function"); - cJSON_AddStringToObject(t42_fn, "name", "context_tasks"); + cJSON_AddStringToObject(t42_fn, "name", "task_list"); cJSON_AddStringToObject(t42_fn, "description", "Build current task list context block from tasks.json"); cJSON_AddStringToObject(t42_params, "type", "object"); cJSON_AddItemToObject(t42_params, "properties", t42_props); @@ -2025,27 +2025,13 @@ char* tools_build_openai_schema_json(const tools_context_t* ctx) { cJSON_AddItemToObject(t42, "function", t42_fn); cJSON_AddItemToArray(tools, t42); - cJSON* t43 = cJSON_CreateObject(); - cJSON* t43_fn = cJSON_CreateObject(); - cJSON* t43_params = cJSON_CreateObject(); - cJSON* t43_props = cJSON_CreateObject(); - - cJSON_AddStringToObject(t43, "type", "function"); - cJSON_AddStringToObject(t43_fn, "name", "context_user_message"); - cJSON_AddStringToObject(t43_fn, "description", "Return current user message text for context template conversation section"); - cJSON_AddStringToObject(t43_params, "type", "object"); - cJSON_AddItemToObject(t43_params, "properties", t43_props); - cJSON_AddItemToObject(t43_fn, "parameters", t43_params); - cJSON_AddItemToObject(t43, "function", t43_fn); - cJSON_AddItemToArray(tools, t43); - cJSON* t44 = cJSON_CreateObject(); cJSON* t44_fn = cJSON_CreateObject(); cJSON* t44_params = cJSON_CreateObject(); cJSON* t44_props = cJSON_CreateObject(); cJSON_AddStringToObject(t44, "type", "function"); - cJSON_AddStringToObject(t44_fn, "name", "context_agent_identity"); + cJSON_AddStringToObject(t44_fn, "name", "agent_identity"); cJSON_AddStringToObject(t44_fn, "description", "Build agent identity context block with pubkey and npub"); cJSON_AddStringToObject(t44_params, "type", "object"); cJSON_AddItemToObject(t44_params, "properties", t44_props); @@ -4216,7 +4202,7 @@ static char* execute_nostr_npub(tools_context_t* ctx, const char* args_json) { return json; } -static char* execute_context_admin_identity(tools_context_t* ctx, const char* args_json) { +static char* execute_admin_identity(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4252,7 +4238,7 @@ static char* execute_context_admin_identity(tools_context_t* ctx, const char* ar return json; } -static char* execute_context_admin_profile(tools_context_t* ctx, const char* args_json) { +static char* execute_nostr_admin_profile(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4291,7 +4277,7 @@ static char* execute_context_admin_profile(tools_context_t* ctx, const char* arg return json; } -static char* execute_context_admin_contacts(tools_context_t* ctx, const char* args_json) { +static char* execute_nostr_admin_contacts(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4330,7 +4316,7 @@ static char* execute_context_admin_contacts(tools_context_t* ctx, const char* ar return json; } -static char* execute_context_admin_relays(tools_context_t* ctx, const char* args_json) { +static char* execute_nostr_admin_relays(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4369,7 +4355,7 @@ static char* execute_context_admin_relays(tools_context_t* ctx, const char* args return json; } -static char* execute_context_admin_notes(tools_context_t* ctx, const char* args_json) { +static char* execute_nostr_admin_notes(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4410,17 +4396,17 @@ static char* execute_context_admin_notes(tools_context_t* ctx, const char* args_ static char* execute_task_manage(tools_context_t* ctx, const char* args_json); -static char* execute_context_tasks(tools_context_t* ctx, const char* args_json) { +static char* execute_task_list(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); char* listed = execute_task_manage(ctx, "{\"action\":\"list\"}"); - if (!listed) return json_error("context_tasks failed to list tasks"); + if (!listed) return json_error("task_list failed to list tasks"); cJSON* listed_root = cJSON_Parse(listed); if (!listed_root || !cJSON_IsObject(listed_root)) { cJSON_Delete(listed_root); free(listed); - return json_error("context_tasks received invalid task_manage response"); + return json_error("task_list received invalid task_manage response"); } cJSON* tasks = cJSON_GetObjectItemCaseSensitive(listed_root, "tasks"); @@ -4533,7 +4519,7 @@ static char* execute_context_tasks(tools_context_t* ctx, const char* args_json) return json; } -static char* execute_context_user_message(tools_context_t* ctx, const char* args_json) { +static char* execute_message_current(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4549,7 +4535,7 @@ static char* execute_context_user_message(tools_context_t* ctx, const char* args return json; } -static char* execute_context_agent_identity(tools_context_t* ctx, const char* args_json) { +static char* execute_agent_identity(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4580,7 +4566,7 @@ static char* execute_context_agent_identity(tools_context_t* ctx, const char* ar return json; } -static char* execute_my_version(const char* args_json) { +static char* execute_agent_version(const char* args_json) { cJSON* args = parse_tool_args_json(args_json); if (!args) return json_error("invalid arguments JSON"); cJSON_Delete(args); @@ -4602,7 +4588,7 @@ static char* execute_my_version(const char* args_json) { return json; } -static char* execute_http_fetch(tools_context_t* ctx, const char* args_json) { +static char* execute_local_http_fetch(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = parse_tool_args_json(args_json); @@ -4617,15 +4603,15 @@ static char* execute_http_fetch(tools_context_t* ctx, const char* args_json) { if (!url || !cJSON_IsString(url) || !url->valuestring || url->valuestring[0] == '\0') { cJSON_Delete(args); - return json_error("http_fetch requires string url"); + return json_error("local_http_fetch requires string url"); } if (headers && !cJSON_IsArray(headers)) { cJSON_Delete(args); - return json_error("http_fetch headers must be an array when provided"); + return json_error("local_http_fetch headers must be an array when provided"); } if (body && !cJSON_IsString(body)) { cJSON_Delete(args); - return json_error("http_fetch body must be a string when provided"); + return json_error("local_http_fetch body must be a string when provided"); } const char* method_str = (method && cJSON_IsString(method) && method->valuestring && method->valuestring[0] != '\0') @@ -4634,7 +4620,7 @@ static char* execute_http_fetch(tools_context_t* ctx, const char* args_json) { if (body && cJSON_IsString(body) && body->valuestring && strcasecmp(method_str, "GET") == 0) { cJSON_Delete(args); - return json_error("http_fetch GET requests cannot include body"); + return json_error("local_http_fetch GET requests cannot include body"); } int timeout_seconds = (timeout && cJSON_IsNumber(timeout)) ? (int)timeout->valuedouble : 20; @@ -4648,10 +4634,10 @@ static char* execute_http_fetch(tools_context_t* ctx, const char* args_json) { CURL* curl = curl_easy_init(); if (!curl) { cJSON_Delete(args); - return json_error("http_fetch failed to initialize curl"); + return json_error("local_http_fetch failed to initialize curl"); } - http_fetch_buffer_t rb; + local_http_fetch_buffer_t rb; memset(&rb, 0, sizeof(rb)); rb.max_bytes = (size_t)max_bytes; @@ -4671,10 +4657,10 @@ static char* execute_http_fetch(tools_context_t* ctx, const char* args_json) { curl_easy_setopt(curl, CURLOPT_URL, url->valuestring); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout_seconds); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_fetch_write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, local_http_fetch_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &rb); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_headers); - curl_easy_setopt(curl, CURLOPT_USERAGENT, "didactyl/http_fetch"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "didactyl/local_http_fetch"); const char* ca_bundle = detect_ca_bundle_path_for_tools(); if (ca_bundle) { @@ -4746,7 +4732,7 @@ static char* execute_http_fetch(tools_context_t* ctx, const char* args_json) { return json; } -static char* execute_shell_exec(tools_context_t* ctx, const char* args_json) { +static char* execute_local_shell_exec(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); if (!ctx->cfg->tools.shell.enabled) return json_error("shell tool disabled"); @@ -4756,7 +4742,7 @@ static char* execute_shell_exec(tools_context_t* ctx, const char* args_json) { cJSON* command = cJSON_GetObjectItemCaseSensitive(args, "command"); if (!command || !cJSON_IsString(command) || !command->valuestring || command->valuestring[0] == '\0') { cJSON_Delete(args); - return json_error("shell_exec requires string command"); + return json_error("local_shell_exec requires string command"); } const char* cwd = ctx->cfg->tools.shell.working_directory[0] != '\0' @@ -4847,7 +4833,7 @@ static char* execute_shell_exec(tools_context_t* ctx, const char* args_json) { return json; } -static char* execute_file_read(tools_context_t* ctx, const char* args_json) { +static char* execute_local_file_read(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = cJSON_Parse(args_json ? args_json : "{}"); @@ -4857,7 +4843,7 @@ static char* execute_file_read(tools_context_t* ctx, const char* args_json) { cJSON* maxb = cJSON_GetObjectItemCaseSensitive(args, "max_bytes"); if (!path || !cJSON_IsString(path) || !path->valuestring) { cJSON_Delete(args); - return json_error("file_read requires string path"); + return json_error("local_file_read requires string path"); } int hard_max = ctx->cfg->tools.shell.max_output_bytes > 0 ? ctx->cfg->tools.shell.max_output_bytes : 65536; @@ -4867,12 +4853,12 @@ static char* execute_file_read(tools_context_t* ctx, const char* args_json) { char file_path[PATH_MAX]; if (build_tool_path(ctx, path->valuestring, file_path, sizeof(file_path)) != 0) { cJSON_Delete(args); - return json_error("file_read path is not allowed"); + return json_error("local_file_read path is not allowed"); } FILE* fp = fopen(file_path, "rb"); cJSON_Delete(args); - if (!fp) return json_error("file_read failed to open file"); + if (!fp) return json_error("local_file_read failed to open file"); char* buf = (char*)calloc((size_t)max_bytes + 1U, 1U); if (!buf) { @@ -4903,7 +4889,7 @@ static char* execute_file_read(tools_context_t* ctx, const char* args_json) { return json; } -static char* execute_file_write(tools_context_t* ctx, const char* args_json) { +static char* execute_local_file_write(tools_context_t* ctx, const char* args_json) { if (!ctx || !ctx->cfg) return json_error("tool context unavailable"); cJSON* args = cJSON_Parse(args_json ? args_json : "{}"); @@ -4915,13 +4901,13 @@ static char* execute_file_write(tools_context_t* ctx, const char* args_json) { if (!path || !cJSON_IsString(path) || !path->valuestring || !content || !cJSON_IsString(content) || !content->valuestring) { cJSON_Delete(args); - return json_error("file_write requires string path and content"); + return json_error("local_file_write requires string path and content"); } char file_path[PATH_MAX]; if (build_tool_path(ctx, path->valuestring, file_path, sizeof(file_path)) != 0) { cJSON_Delete(args); - return json_error("file_write path is not allowed"); + return json_error("local_file_write path is not allowed"); } const char* content_str = content->valuestring; @@ -4930,11 +4916,11 @@ static char* execute_file_write(tools_context_t* ctx, const char* args_json) { FILE* fp = fopen(file_path, do_append ? "ab" : "wb"); cJSON_Delete(args); - if (!fp) return json_error("file_write failed to open file"); + if (!fp) return json_error("local_file_write failed to open file"); size_t n = fwrite(content_str, 1, len, fp); fclose(fp); - if (n != len) return json_error("file_write failed to write all bytes"); + if (n != len) return json_error("local_file_write failed to write all bytes"); cJSON* out = cJSON_CreateObject(); if (!out) return NULL; @@ -5605,8 +5591,8 @@ char* tools_execute(tools_context_t* ctx, const char* tool_name, const char* arg if (strcmp(tool_name, "nostr_query") == 0) { return execute_nostr_query(args_json); } - if (strcmp(tool_name, "my_version") == 0) { - return execute_my_version(args_json); + if (strcmp(tool_name, "agent_version") == 0) { + return execute_agent_version(args_json); } if (strcmp(tool_name, "nostr_pubkey") == 0) { return execute_nostr_pubkey(ctx, args_json); @@ -5620,17 +5606,17 @@ char* tools_execute(tools_context_t* ctx, const char* tool_name, const char* arg if (strcmp(tool_name, "my_npub") == 0) { return execute_nostr_npub(ctx, args_json); } - if (strcmp(tool_name, "http_fetch") == 0) { - return execute_http_fetch(ctx, args_json); + if (strcmp(tool_name, "local_http_fetch") == 0) { + return execute_local_http_fetch(ctx, args_json); } - if (strcmp(tool_name, "shell_exec") == 0) { - return execute_shell_exec(ctx, args_json); + if (strcmp(tool_name, "local_shell_exec") == 0) { + return execute_local_shell_exec(ctx, args_json); } - if (strcmp(tool_name, "file_read") == 0) { - return execute_file_read(ctx, args_json); + if (strcmp(tool_name, "local_file_read") == 0) { + return execute_local_file_read(ctx, args_json); } - if (strcmp(tool_name, "file_write") == 0) { - return execute_file_write(ctx, args_json); + if (strcmp(tool_name, "local_file_write") == 0) { + return execute_local_file_write(ctx, args_json); } if (strcmp(tool_name, "skill_create") == 0) { return execute_skill_create(ctx, args_json); @@ -5672,29 +5658,29 @@ char* tools_execute(tools_context_t* ctx, const char* tool_name, const char* arg if (strcmp(tool_name, "task_manage") == 0) { return execute_task_manage(ctx, args_json); } - if (strcmp(tool_name, "context_admin_identity") == 0) { - return execute_context_admin_identity(ctx, args_json); + if (strcmp(tool_name, "admin_identity") == 0) { + return execute_admin_identity(ctx, args_json); } - if (strcmp(tool_name, "context_admin_profile") == 0) { - return execute_context_admin_profile(ctx, args_json); + if (strcmp(tool_name, "nostr_admin_profile") == 0) { + return execute_nostr_admin_profile(ctx, args_json); } - if (strcmp(tool_name, "context_admin_contacts") == 0) { - return execute_context_admin_contacts(ctx, args_json); + if (strcmp(tool_name, "nostr_admin_contacts") == 0) { + return execute_nostr_admin_contacts(ctx, args_json); } - if (strcmp(tool_name, "context_admin_relays") == 0) { - return execute_context_admin_relays(ctx, args_json); + if (strcmp(tool_name, "nostr_admin_relays") == 0) { + return execute_nostr_admin_relays(ctx, args_json); } - if (strcmp(tool_name, "context_admin_notes") == 0) { - return execute_context_admin_notes(ctx, args_json); + if (strcmp(tool_name, "nostr_admin_notes") == 0) { + return execute_nostr_admin_notes(ctx, args_json); } - if (strcmp(tool_name, "context_tasks") == 0) { - return execute_context_tasks(ctx, args_json); + if (strcmp(tool_name, "task_list") == 0) { + return execute_task_list(ctx, args_json); } - if (strcmp(tool_name, "context_user_message") == 0) { - return execute_context_user_message(ctx, args_json); + if (strcmp(tool_name, "message_current") == 0) { + return execute_message_current(ctx, args_json); } - if (strcmp(tool_name, "context_agent_identity") == 0) { - return execute_context_agent_identity(ctx, args_json); + if (strcmp(tool_name, "agent_identity") == 0) { + return execute_agent_identity(ctx, args_json); } return json_error("unknown tool");