v0.0.67 - Add agent self-context subscriptions, tools, aliases, and prompt template sections
This commit is contained in:
@@ -55,11 +55,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.
|
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.66
|
## Current Status — v0.0.67
|
||||||
|
|
||||||
**Active build — this project is barely working. Experiment at your own risk.**
|
**Active build — this project is barely working. Experiment at your own risk.**
|
||||||
|
|
||||||
> Last release update: v0.0.66 — Complete tools refactor, move all tools-named sources/headers into src/tools, and update build wiring
|
> Last release update: v0.0.67 — Add agent self-context subscriptions, tools, aliases, and prompt template sections
|
||||||
|
|
||||||
- Connects to configured relays with auto-reconnect and relay state transition logging
|
- Connects to configured relays with auto-reconnect and relay state transition logging
|
||||||
- Publishes configured startup events per relay as each relay becomes connected
|
- Publishes configured startup events per relay as each relay becomes connected
|
||||||
|
|||||||
@@ -31,6 +31,25 @@
|
|||||||
"temperature": 0.7 // sampling temperature (0.0 – 2.0)
|
"temperature": 0.7 // sampling temperature (0.0 – 2.0)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ─── Tools & Runtime Limits ───────────────────────────────────────
|
||||||
|
// Centralized turn and timeout limits used by DM agent loops,
|
||||||
|
// triggered skills, HTTP API prompt runs, and local HTTP fetch tool.
|
||||||
|
"tools": {
|
||||||
|
"enabled": true,
|
||||||
|
"max_turns": 8, // default max turns for normal agent tool loops
|
||||||
|
"trigger_max_turns": 6, // max turns for triggered-skill executions
|
||||||
|
"api_default_max_turns": 4, // default when HTTP body omits max_turns
|
||||||
|
"api_max_turns_ceiling": 16, // hard cap applied to API-provided max_turns
|
||||||
|
"local_http_fetch_default_timeout_seconds": 20,
|
||||||
|
"local_http_fetch_max_timeout_seconds": 120,
|
||||||
|
"shell": {
|
||||||
|
"enabled": true,
|
||||||
|
"timeout_seconds": 30,
|
||||||
|
"max_output_bytes": 65536,
|
||||||
|
"working_directory": "."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// ─── Security Tiers ───────────────────────────────────────────────
|
// ─── Security Tiers ───────────────────────────────────────────────
|
||||||
// Controls who can interact with the agent and what they can do.
|
// Controls who can interact with the agent and what they can do.
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
@@ -26,6 +26,31 @@
|
|||||||
tool: nostr_admin_notes
|
tool: nostr_admin_notes
|
||||||
skip_if_empty: true
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_identity
|
||||||
|
role: system
|
||||||
|
tool: agent_identity
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_profile
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_profile
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_contacts
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_contacts
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_relays
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_relays
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_notes
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_notes
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
- section: tasks
|
- section: tasks
|
||||||
role: system
|
role: system
|
||||||
tool: task_list
|
tool: task_list
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
# Agent Self-Context: Know Thyself
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
When the agent is asked about itself — its profile, contacts, relays, or recent notes — it has no idea. The context template currently injects **administrator** identity/profile/contacts/relays/notes into the system prompt, but nothing equivalent for the **agent's own** Nostr identity beyond a bare pubkey+npub from `agent_identity`.
|
||||||
|
|
||||||
|
The agent publishes its own kind 0 (profile), kind 3 (contacts), kind 10002 (relays), and kind 1 (notes) at startup, but never subscribes to or caches those events for self-awareness.
|
||||||
|
|
||||||
|
## Solution Overview
|
||||||
|
|
||||||
|
Mirror the admin context pattern for the agent itself:
|
||||||
|
|
||||||
|
1. **Subscribe** to the agent's own kind 0/3/10002/1 events from relays
|
||||||
|
2. **Cache** them in `nostr_handler.c` (same pattern as `g_admin_kind0_json` etc.)
|
||||||
|
3. **Expose** them via new `nostr_handler_get_agent_*` API functions
|
||||||
|
4. **Create context tools** that format the cached data into context blocks
|
||||||
|
5. **Add to context template** so the agent always knows about itself
|
||||||
|
6. **Add callable tool aliases** like `my_kind0_profile` for on-demand use
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[Startup: publish kind 0/3/10002/1] --> B[Subscribe to own events]
|
||||||
|
B --> C[Cache in nostr_handler globals]
|
||||||
|
C --> D[Context tools read cache]
|
||||||
|
D --> E[Context template injects into system prompt]
|
||||||
|
D --> F[LLM can call tools on-demand]
|
||||||
|
|
||||||
|
subgraph Admin Context - existing
|
||||||
|
G[g_admin_kind0_json]
|
||||||
|
H[g_admin_kind3 contacts]
|
||||||
|
I[g_admin_kind10002_json]
|
||||||
|
J[g_admin_kind1_notes]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Agent Context - new
|
||||||
|
K[g_agent_kind0_json]
|
||||||
|
L[g_agent_kind3 contacts]
|
||||||
|
M[g_agent_kind10002_json]
|
||||||
|
N[g_agent_kind1_notes]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detailed Changes
|
||||||
|
|
||||||
|
### 1. nostr_handler.c — Agent Self-Context Cache
|
||||||
|
|
||||||
|
Add new static globals mirroring the admin pattern:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static char* g_agent_kind0_json = NULL; // kind 0 profile JSON
|
||||||
|
static char* g_agent_kind10002_json = NULL; // kind 10002 relay list JSON
|
||||||
|
static char** g_agent_kind3_contacts = NULL; // kind 3 contact pubkeys
|
||||||
|
static int g_agent_kind3_contact_count = 0;
|
||||||
|
static admin_kind1_note_t* g_agent_kind1_notes = NULL; // reuse struct
|
||||||
|
static int g_agent_kind1_note_count = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. nostr_handler.c — Agent Self-Context Subscription
|
||||||
|
|
||||||
|
Create `nostr_handler_subscribe_agent_context()` that subscribes to kinds 0, 3, 10002, 1 filtered by the agent's own pubkey. This is separate from the self-skills subscription (which handles 31123/31124/10123).
|
||||||
|
|
||||||
|
The callback `on_agent_context_event()` will parse and cache events using the same logic as `on_admin_context_event()`.
|
||||||
|
|
||||||
|
**Alternative considered:** Expanding `nostr_handler_subscribe_self_skills()` to include these kinds. Rejected because the self-skills sub has different EOSE handling and a callback for skill loading. Keeping them separate is cleaner.
|
||||||
|
|
||||||
|
### 3. nostr_handler.h — New API Functions
|
||||||
|
|
||||||
|
```c
|
||||||
|
int nostr_handler_subscribe_agent_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind0_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind3_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind10002_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind1_notes_context(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. main.c — Call Agent Context Subscription at Startup
|
||||||
|
|
||||||
|
Add `nostr_handler_subscribe_agent_context()` call after admin context subscription, before self-skills subscription.
|
||||||
|
|
||||||
|
### 5. tool_agent.c — New Context Tools
|
||||||
|
|
||||||
|
Create four new tool functions following the exact pattern from `tool_admin.c`:
|
||||||
|
|
||||||
|
| Tool Name | Description | Data Source |
|
||||||
|
|-----------|-------------|-------------|
|
||||||
|
| `nostr_agent_profile` | Agent's kind 0 profile metadata | `nostr_handler_get_agent_kind0_context()` |
|
||||||
|
| `nostr_agent_contacts` | Agent's kind 3 contact list | `nostr_handler_get_agent_kind3_context()` |
|
||||||
|
| `nostr_agent_relays` | Agent's kind 10002 relay list | `nostr_handler_get_agent_kind10002_context()` |
|
||||||
|
| `nostr_agent_notes` | Agent's recent kind 1 notes | `nostr_handler_get_agent_kind1_notes_context()` |
|
||||||
|
|
||||||
|
Each returns a `content` field with markdown-formatted context, e.g.:
|
||||||
|
```
|
||||||
|
## Agent Kind 0 Profile (source: nostr kind 0)
|
||||||
|
|
||||||
|
Agent kind 0 profile content (JSON): {"name":"Didactyl Agent","display_name":"Didactyl",...}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. tools_internal.h — Declare New Functions
|
||||||
|
|
||||||
|
```c
|
||||||
|
char* execute_nostr_agent_profile(tools_context_t* ctx, const char* args_json);
|
||||||
|
char* execute_nostr_agent_contacts(tools_context_t* ctx, const char* args_json);
|
||||||
|
char* execute_nostr_agent_relays(tools_context_t* ctx, const char* args_json);
|
||||||
|
char* execute_nostr_agent_notes(tools_context_t* ctx, const char* args_json);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. tools_dispatch.c — Register + Aliases
|
||||||
|
|
||||||
|
Add dispatch entries:
|
||||||
|
```c
|
||||||
|
if (strcmp(tool_name, "nostr_agent_profile") == 0) return execute_nostr_agent_profile(ctx, args_json);
|
||||||
|
if (strcmp(tool_name, "nostr_agent_contacts") == 0) return execute_nostr_agent_contacts(ctx, args_json);
|
||||||
|
if (strcmp(tool_name, "nostr_agent_relays") == 0) return execute_nostr_agent_relays(ctx, args_json);
|
||||||
|
if (strcmp(tool_name, "nostr_agent_notes") == 0) return execute_nostr_agent_notes(ctx, args_json);
|
||||||
|
|
||||||
|
// Friendly aliases
|
||||||
|
if (strcmp(tool_name, "my_kind0_profile") == 0) return execute_nostr_agent_profile(ctx, args_json);
|
||||||
|
if (strcmp(tool_name, "my_contacts") == 0) return execute_nostr_agent_contacts(ctx, args_json);
|
||||||
|
if (strcmp(tool_name, "my_relays") == 0) return execute_nostr_agent_relays(ctx, args_json);
|
||||||
|
if (strcmp(tool_name, "my_notes") == 0) return execute_nostr_agent_notes(ctx, args_json);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. tools_schema.c — OpenAI Function Schemas
|
||||||
|
|
||||||
|
Add 8 new tool schemas (4 canonical + 4 aliases), all with empty parameters (no-arg tools), following the pattern of `admin_identity`/`nostr_admin_profile` etc.
|
||||||
|
|
||||||
|
### 9. Context Template Update
|
||||||
|
|
||||||
|
Update the kind 31120 soul content in `config.jsonc` and `context_template.md` to add agent sections **after** admin sections:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- section: admin_notes
|
||||||
|
role: system
|
||||||
|
tool: nostr_admin_notes
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
# NEW: Agent self-context sections
|
||||||
|
- section: agent_identity
|
||||||
|
role: system
|
||||||
|
tool: agent_identity
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_profile
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_profile
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_contacts
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_contacts
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_relays
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_relays
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: agent_notes
|
||||||
|
role: system
|
||||||
|
tool: nostr_agent_notes
|
||||||
|
skip_if_empty: true
|
||||||
|
|
||||||
|
- section: tasks
|
||||||
|
role: system
|
||||||
|
tool: task_list
|
||||||
|
skip_if_empty: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10. nostr_handler.c Cleanup
|
||||||
|
|
||||||
|
Add cleanup for agent context globals in `nostr_handler_cleanup()`, mirroring `free_admin_context_locked()`.
|
||||||
|
|
||||||
|
## File Change Summary
|
||||||
|
|
||||||
|
| File | Change Type | Description |
|
||||||
|
|------|-------------|-------------|
|
||||||
|
| `src/nostr_handler.h` | Modify | Add 5 new function declarations |
|
||||||
|
| `src/nostr_handler.c` | Modify | Add agent context cache globals, subscription, event handler, getter functions, cleanup |
|
||||||
|
| `src/main.c` | Modify | Call `nostr_handler_subscribe_agent_context()` at startup |
|
||||||
|
| `src/tools/tool_agent.c` | Modify | Add 4 new context tool execute functions |
|
||||||
|
| `src/tools/tools_internal.h` | Modify | Declare 4 new execute functions |
|
||||||
|
| `src/tools/tools_dispatch.c` | Modify | Add 8 dispatch entries (4 tools + 4 aliases) |
|
||||||
|
| `src/tools/tools_schema.c` | Modify | Add 8 OpenAI function schemas |
|
||||||
|
| `config.jsonc` | Modify | Update kind 31120 template section |
|
||||||
|
| `config.jsonc.example` | Modify | Update kind 31120 template section |
|
||||||
|
| `context_template.md` | Modify | Add agent_* sections after admin_* sections |
|
||||||
|
|
||||||
|
## Context Token Impact
|
||||||
|
|
||||||
|
Each agent context section adds roughly the same token count as its admin counterpart:
|
||||||
|
- agent_identity: ~40 tokens (already exists, just adding to template)
|
||||||
|
- agent_profile: ~80-150 tokens (depends on profile richness)
|
||||||
|
- agent_contacts: ~50-200 tokens (depends on contact count)
|
||||||
|
- agent_relays: ~50-100 tokens
|
||||||
|
- agent_notes: ~100-300 tokens (depends on note count/length)
|
||||||
|
|
||||||
|
Total additional context: ~320-790 tokens. With `skip_if_empty: true`, empty sections cost 0 tokens.
|
||||||
|
|
||||||
|
## Design Decisions
|
||||||
|
|
||||||
|
1. **Separate subscription vs expanding self-skills sub**: Separate is cleaner — different EOSE semantics, different callback needs.
|
||||||
|
2. **Cache from relay vs read from config**: Cache from relay is more accurate (reflects what's actually published, not just what config says). The startup_events in config are the *intent*; the relay data is the *reality*.
|
||||||
|
3. **Alias naming**: `my_kind0_profile` matches the existing `my_npub`/`my_pubkey` pattern. Also adding `my_contacts`, `my_relays`, `my_notes` for consistency.
|
||||||
|
4. **No new config section needed**: The agent context subscription is unconditional — an agent should always know about itself. No `agent_context.enabled` toggle needed.
|
||||||
+3
-1
@@ -1987,7 +1987,9 @@ void agent_on_trigger(const char* skill_d_tag,
|
|||||||
free(system_prompt);
|
free(system_prompt);
|
||||||
free(user_prompt);
|
free(user_prompt);
|
||||||
|
|
||||||
int max_turns = g_cfg->tools.max_turns > 0 ? g_cfg->tools.max_turns : 6;
|
int max_turns = g_cfg->tools.trigger_max_turns > 0
|
||||||
|
? g_cfg->tools.trigger_max_turns
|
||||||
|
: (g_cfg->tools.max_turns > 0 ? g_cfg->tools.max_turns : 8);
|
||||||
|
|
||||||
for (int turn = 0; turn < max_turns; turn++) {
|
for (int turn = 0; turn < max_turns; turn++) {
|
||||||
char* messages_json = cJSON_PrintUnformatted(messages);
|
char* messages_json = cJSON_PrintUnformatted(messages);
|
||||||
|
|||||||
@@ -222,12 +222,36 @@ static int parse_tools_config(cJSON* root, didactyl_config_t* config) {
|
|||||||
|
|
||||||
cJSON* enabled = cJSON_GetObjectItemCaseSensitive(tools, "enabled");
|
cJSON* enabled = cJSON_GetObjectItemCaseSensitive(tools, "enabled");
|
||||||
cJSON* max_turns = cJSON_GetObjectItemCaseSensitive(tools, "max_turns");
|
cJSON* max_turns = cJSON_GetObjectItemCaseSensitive(tools, "max_turns");
|
||||||
|
cJSON* trigger_max_turns = cJSON_GetObjectItemCaseSensitive(tools, "trigger_max_turns");
|
||||||
|
cJSON* api_default_max_turns = cJSON_GetObjectItemCaseSensitive(tools, "api_default_max_turns");
|
||||||
|
cJSON* api_max_turns_ceiling = cJSON_GetObjectItemCaseSensitive(tools, "api_max_turns_ceiling");
|
||||||
|
cJSON* local_http_fetch_default_timeout_seconds =
|
||||||
|
cJSON_GetObjectItemCaseSensitive(tools, "local_http_fetch_default_timeout_seconds");
|
||||||
|
cJSON* local_http_fetch_max_timeout_seconds =
|
||||||
|
cJSON_GetObjectItemCaseSensitive(tools, "local_http_fetch_max_timeout_seconds");
|
||||||
if (enabled && cJSON_IsBool(enabled)) {
|
if (enabled && cJSON_IsBool(enabled)) {
|
||||||
config->tools.enabled = cJSON_IsTrue(enabled) ? 1 : 0;
|
config->tools.enabled = cJSON_IsTrue(enabled) ? 1 : 0;
|
||||||
}
|
}
|
||||||
if (max_turns && cJSON_IsNumber(max_turns)) {
|
if (max_turns && cJSON_IsNumber(max_turns)) {
|
||||||
config->tools.max_turns = (int)max_turns->valuedouble;
|
config->tools.max_turns = (int)max_turns->valuedouble;
|
||||||
}
|
}
|
||||||
|
if (trigger_max_turns && cJSON_IsNumber(trigger_max_turns)) {
|
||||||
|
config->tools.trigger_max_turns = (int)trigger_max_turns->valuedouble;
|
||||||
|
}
|
||||||
|
if (api_default_max_turns && cJSON_IsNumber(api_default_max_turns)) {
|
||||||
|
config->tools.api_default_max_turns = (int)api_default_max_turns->valuedouble;
|
||||||
|
}
|
||||||
|
if (api_max_turns_ceiling && cJSON_IsNumber(api_max_turns_ceiling)) {
|
||||||
|
config->tools.api_max_turns_ceiling = (int)api_max_turns_ceiling->valuedouble;
|
||||||
|
}
|
||||||
|
if (local_http_fetch_default_timeout_seconds && cJSON_IsNumber(local_http_fetch_default_timeout_seconds)) {
|
||||||
|
config->tools.local_http_fetch_default_timeout_seconds =
|
||||||
|
(int)local_http_fetch_default_timeout_seconds->valuedouble;
|
||||||
|
}
|
||||||
|
if (local_http_fetch_max_timeout_seconds && cJSON_IsNumber(local_http_fetch_max_timeout_seconds)) {
|
||||||
|
config->tools.local_http_fetch_max_timeout_seconds =
|
||||||
|
(int)local_http_fetch_max_timeout_seconds->valuedouble;
|
||||||
|
}
|
||||||
|
|
||||||
cJSON* shell = cJSON_GetObjectItemCaseSensitive(tools, "shell");
|
cJSON* shell = cJSON_GetObjectItemCaseSensitive(tools, "shell");
|
||||||
if (!shell || !cJSON_IsObject(shell)) {
|
if (!shell || !cJSON_IsObject(shell)) {
|
||||||
@@ -255,6 +279,31 @@ static int parse_tools_config(cJSON* root, didactyl_config_t* config) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config->tools.max_turns < 1) {
|
||||||
|
config->tools.max_turns = 8;
|
||||||
|
}
|
||||||
|
if (config->tools.trigger_max_turns < 1) {
|
||||||
|
config->tools.trigger_max_turns = config->tools.max_turns;
|
||||||
|
}
|
||||||
|
if (config->tools.api_default_max_turns < 1) {
|
||||||
|
config->tools.api_default_max_turns = config->tools.max_turns;
|
||||||
|
}
|
||||||
|
if (config->tools.api_max_turns_ceiling < 1) {
|
||||||
|
config->tools.api_max_turns_ceiling = 16;
|
||||||
|
}
|
||||||
|
if (config->tools.api_default_max_turns > config->tools.api_max_turns_ceiling) {
|
||||||
|
config->tools.api_default_max_turns = config->tools.api_max_turns_ceiling;
|
||||||
|
}
|
||||||
|
if (config->tools.local_http_fetch_default_timeout_seconds < 1) {
|
||||||
|
config->tools.local_http_fetch_default_timeout_seconds = 20;
|
||||||
|
}
|
||||||
|
if (config->tools.local_http_fetch_max_timeout_seconds < 1) {
|
||||||
|
config->tools.local_http_fetch_max_timeout_seconds = 120;
|
||||||
|
}
|
||||||
|
if (config->tools.local_http_fetch_default_timeout_seconds > config->tools.local_http_fetch_max_timeout_seconds) {
|
||||||
|
config->tools.local_http_fetch_default_timeout_seconds = config->tools.local_http_fetch_max_timeout_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,6 +795,11 @@ int config_load(const char* path, didactyl_config_t* config) {
|
|||||||
|
|
||||||
config->tools.enabled = 1;
|
config->tools.enabled = 1;
|
||||||
config->tools.max_turns = 8;
|
config->tools.max_turns = 8;
|
||||||
|
config->tools.trigger_max_turns = 6;
|
||||||
|
config->tools.api_default_max_turns = 4;
|
||||||
|
config->tools.api_max_turns_ceiling = 16;
|
||||||
|
config->tools.local_http_fetch_default_timeout_seconds = 20;
|
||||||
|
config->tools.local_http_fetch_max_timeout_seconds = 120;
|
||||||
config->tools.shell.enabled = 1;
|
config->tools.shell.enabled = 1;
|
||||||
config->tools.shell.timeout_seconds = 30;
|
config->tools.shell.timeout_seconds = 30;
|
||||||
config->tools.shell.max_output_bytes = 65536;
|
config->tools.shell.max_output_bytes = 65536;
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int enabled;
|
int enabled;
|
||||||
int max_turns;
|
int max_turns;
|
||||||
|
int trigger_max_turns;
|
||||||
|
int api_default_max_turns;
|
||||||
|
int api_max_turns_ceiling;
|
||||||
|
int local_http_fetch_default_timeout_seconds;
|
||||||
|
int local_http_fetch_max_timeout_seconds;
|
||||||
shell_tools_config_t shell;
|
shell_tools_config_t shell;
|
||||||
} tools_config_t;
|
} tools_config_t;
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -755,8 +755,13 @@ static cJSON* run_prompt_with_tools_convo(cJSON* convo,
|
|||||||
const char* tool_limit_message) {
|
const char* tool_limit_message) {
|
||||||
if (!convo || !cJSON_IsArray(convo)) return NULL;
|
if (!convo || !cJSON_IsArray(convo)) return NULL;
|
||||||
|
|
||||||
|
int max_ceiling = 16;
|
||||||
|
if (g_api_ctx.cfg && g_api_ctx.cfg->tools.api_max_turns_ceiling > 0) {
|
||||||
|
max_ceiling = g_api_ctx.cfg->tools.api_max_turns_ceiling;
|
||||||
|
}
|
||||||
|
|
||||||
if (max_turns < 1) max_turns = 1;
|
if (max_turns < 1) max_turns = 1;
|
||||||
if (max_turns > 16) max_turns = 16;
|
if (max_turns > max_ceiling) max_turns = max_ceiling;
|
||||||
|
|
||||||
char* tools_json = tools_build_openai_schema_json(g_api_ctx.tools_ctx);
|
char* tools_json = tools_build_openai_schema_json(g_api_ctx.tools_ctx);
|
||||||
if (!tools_json) {
|
if (!tools_json) {
|
||||||
@@ -888,6 +893,9 @@ static cJSON* run_prompt_with_tools(cJSON* body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int max_turns = 4;
|
int max_turns = 4;
|
||||||
|
if (g_api_ctx.cfg && g_api_ctx.cfg->tools.api_default_max_turns > 0) {
|
||||||
|
max_turns = g_api_ctx.cfg->tools.api_default_max_turns;
|
||||||
|
}
|
||||||
cJSON* max_turns_node = cJSON_GetObjectItemCaseSensitive(body, "max_turns");
|
cJSON* max_turns_node = cJSON_GetObjectItemCaseSensitive(body, "max_turns");
|
||||||
if (max_turns_node && cJSON_IsNumber(max_turns_node)) {
|
if (max_turns_node && cJSON_IsNumber(max_turns_node)) {
|
||||||
max_turns = (int)max_turns_node->valuedouble;
|
max_turns = (int)max_turns_node->valuedouble;
|
||||||
@@ -1104,6 +1112,9 @@ static void handle_prompt_agent(struct mg_connection* c, const struct mg_http_me
|
|||||||
}
|
}
|
||||||
|
|
||||||
int max_turns = 4;
|
int max_turns = 4;
|
||||||
|
if (g_api_ctx.cfg && g_api_ctx.cfg->tools.api_default_max_turns > 0) {
|
||||||
|
max_turns = g_api_ctx.cfg->tools.api_default_max_turns;
|
||||||
|
}
|
||||||
cJSON* max_turns_node = cJSON_GetObjectItemCaseSensitive(body, "max_turns");
|
cJSON* max_turns_node = cJSON_GetObjectItemCaseSensitive(body, "max_turns");
|
||||||
if (max_turns_node && cJSON_IsNumber(max_turns_node)) {
|
if (max_turns_node && cJSON_IsNumber(max_turns_node)) {
|
||||||
max_turns = (int)max_turns_node->valuedouble;
|
max_turns = (int)max_turns_node->valuedouble;
|
||||||
|
|||||||
@@ -284,6 +284,12 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
DEBUG_INFO("[didactyl] startup phase: subscribe admin context end");
|
DEBUG_INFO("[didactyl] startup phase: subscribe admin context end");
|
||||||
|
|
||||||
|
DEBUG_INFO("[didactyl] startup phase: subscribe agent self context begin");
|
||||||
|
if (nostr_handler_subscribe_agent_context() != 0) {
|
||||||
|
DEBUG_WARN("[didactyl] startup phase: subscribe agent self context failed (continuing)");
|
||||||
|
}
|
||||||
|
DEBUG_INFO("[didactyl] startup phase: subscribe agent self context end");
|
||||||
|
|
||||||
DEBUG_INFO("[didactyl] startup phase: subscribe self skill cache begin");
|
DEBUG_INFO("[didactyl] startup phase: subscribe self skill cache begin");
|
||||||
if (nostr_handler_subscribe_self_skills() != 0) {
|
if (nostr_handler_subscribe_self_skills() != 0) {
|
||||||
DEBUG_WARN("[didactyl] startup phase: subscribe self skill cache failed (continuing)");
|
DEBUG_WARN("[didactyl] startup phase: subscribe self skill cache failed (continuing)");
|
||||||
|
|||||||
+2
-2
@@ -12,8 +12,8 @@
|
|||||||
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||||
#define DIDACTYL_VERSION_MAJOR 0
|
#define DIDACTYL_VERSION_MAJOR 0
|
||||||
#define DIDACTYL_VERSION_MINOR 0
|
#define DIDACTYL_VERSION_MINOR 0
|
||||||
#define DIDACTYL_VERSION_PATCH 66
|
#define DIDACTYL_VERSION_PATCH 67
|
||||||
#define DIDACTYL_VERSION "v0.0.66"
|
#define DIDACTYL_VERSION "v0.0.67"
|
||||||
|
|
||||||
// Agent metadata
|
// Agent metadata
|
||||||
#define DIDACTYL_NAME "Didactyl"
|
#define DIDACTYL_NAME "Didactyl"
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ typedef struct {
|
|||||||
static admin_kind1_note_t* g_admin_kind1_notes = NULL;
|
static admin_kind1_note_t* g_admin_kind1_notes = NULL;
|
||||||
static int g_admin_kind1_note_count = 0;
|
static int g_admin_kind1_note_count = 0;
|
||||||
|
|
||||||
|
static char* g_agent_kind0_json = NULL;
|
||||||
|
static char* g_agent_kind10002_json = NULL;
|
||||||
|
static char** g_agent_contacts = NULL;
|
||||||
|
static int g_agent_contact_count = 0;
|
||||||
|
static admin_kind1_note_t* g_agent_kind1_notes = NULL;
|
||||||
|
static int g_agent_kind1_note_count = 0;
|
||||||
|
|
||||||
static pthread_mutex_t g_admin_ctx_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t g_admin_ctx_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static cJSON* g_self_skill_events = NULL;
|
static cJSON* g_self_skill_events = NULL;
|
||||||
@@ -269,6 +276,11 @@ static void on_admin_context_event(cJSON* event, const char* relay_url, void* us
|
|||||||
static int parse_kind3_wot_contacts(cJSON* tags);
|
static int parse_kind3_wot_contacts(cJSON* tags);
|
||||||
static int parse_kind10002_relays(cJSON* tags);
|
static int parse_kind10002_relays(cJSON* tags);
|
||||||
static void upsert_kind1_note(time_t created_at, const char* content);
|
static void upsert_kind1_note(time_t created_at, const char* content);
|
||||||
|
static void free_agent_context_locked(void);
|
||||||
|
static int parse_kind3_agent_contacts(cJSON* tags);
|
||||||
|
static int parse_kind10002_agent_relays(cJSON* tags);
|
||||||
|
static void upsert_agent_kind1_note(time_t created_at, const char* content);
|
||||||
|
static void on_agent_context_event(cJSON* event, const char* relay_url, void* user_data);
|
||||||
static int startup_self_kind1_exists(void);
|
static int startup_self_kind1_exists(void);
|
||||||
static void load_startup_display_name(void);
|
static void load_startup_display_name(void);
|
||||||
static void build_startup_kind1_content(char* out, size_t out_size, const char* fallback);
|
static void build_startup_kind1_content(char* out, size_t out_size, const char* fallback);
|
||||||
@@ -1114,6 +1126,31 @@ static void free_admin_context_locked(void) {
|
|||||||
g_admin_kind1_note_count = 0;
|
g_admin_kind1_note_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void free_agent_context_locked(void) {
|
||||||
|
free(g_agent_kind0_json);
|
||||||
|
g_agent_kind0_json = NULL;
|
||||||
|
free(g_agent_kind10002_json);
|
||||||
|
g_agent_kind10002_json = NULL;
|
||||||
|
|
||||||
|
if (g_agent_contacts) {
|
||||||
|
for (int i = 0; i < g_agent_contact_count; i++) {
|
||||||
|
free(g_agent_contacts[i]);
|
||||||
|
}
|
||||||
|
free(g_agent_contacts);
|
||||||
|
}
|
||||||
|
g_agent_contacts = NULL;
|
||||||
|
g_agent_contact_count = 0;
|
||||||
|
|
||||||
|
if (g_agent_kind1_notes) {
|
||||||
|
for (int i = 0; i < g_agent_kind1_note_count; i++) {
|
||||||
|
free(g_agent_kind1_notes[i].content);
|
||||||
|
}
|
||||||
|
free(g_agent_kind1_notes);
|
||||||
|
}
|
||||||
|
g_agent_kind1_notes = NULL;
|
||||||
|
g_agent_kind1_note_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_kind3_wot_contacts(cJSON* tags) {
|
static int parse_kind3_wot_contacts(cJSON* tags) {
|
||||||
if (!tags || !cJSON_IsArray(tags)) {
|
if (!tags || !cJSON_IsArray(tags)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1242,6 +1279,134 @@ static void upsert_kind1_note(time_t created_at, const char* content) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_kind3_agent_contacts(cJSON* tags) {
|
||||||
|
if (!tags || !cJSON_IsArray(tags)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_agent_contacts) {
|
||||||
|
for (int i = 0; i < g_agent_contact_count; i++) {
|
||||||
|
free(g_agent_contacts[i]);
|
||||||
|
}
|
||||||
|
free(g_agent_contacts);
|
||||||
|
g_agent_contacts = NULL;
|
||||||
|
g_agent_contact_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = cJSON_GetArraySize(tags);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
cJSON* tag = cJSON_GetArrayItem(tags, i);
|
||||||
|
if (!tag || !cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* key = cJSON_GetArrayItem(tag, 0);
|
||||||
|
cJSON* val = cJSON_GetArrayItem(tag, 1);
|
||||||
|
if (!key || !val || !cJSON_IsString(key) || !cJSON_IsString(val) || !key->valuestring || !val->valuestring) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key->valuestring, "p") != 0 || strlen(val->valuestring) != 64U) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* dup = strdup(val->valuestring);
|
||||||
|
if (!dup) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** grown = (char**)realloc(g_agent_contacts, (size_t)(g_agent_contact_count + 1) * sizeof(char*));
|
||||||
|
if (!grown) {
|
||||||
|
free(dup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_agent_contacts = grown;
|
||||||
|
g_agent_contacts[g_agent_contact_count++] = dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_kind10002_agent_relays(cJSON* tags) {
|
||||||
|
if (!tags || !cJSON_IsArray(tags)) {
|
||||||
|
free(g_agent_kind10002_json);
|
||||||
|
g_agent_kind10002_json = strdup("[]");
|
||||||
|
return g_agent_kind10002_json ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* relays = cJSON_CreateArray();
|
||||||
|
if (!relays) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = cJSON_GetArraySize(tags);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
cJSON* tag = cJSON_GetArrayItem(tags, i);
|
||||||
|
if (!tag || !cJSON_IsArray(tag) || cJSON_GetArraySize(tag) < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* key = cJSON_GetArrayItem(tag, 0);
|
||||||
|
cJSON* val = cJSON_GetArrayItem(tag, 1);
|
||||||
|
if (!key || !val || !cJSON_IsString(key) || !cJSON_IsString(val) || !key->valuestring || !val->valuestring) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key->valuestring, "r") != 0 || val->valuestring[0] == '\0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(relays, cJSON_CreateString(val->valuestring));
|
||||||
|
}
|
||||||
|
|
||||||
|
char* relays_json = cJSON_PrintUnformatted(relays);
|
||||||
|
cJSON_Delete(relays);
|
||||||
|
if (!relays_json) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(g_agent_kind10002_json);
|
||||||
|
g_agent_kind10002_json = relays_json;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void upsert_agent_kind1_note(time_t created_at, const char* content) {
|
||||||
|
if (!content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int limit = g_cfg && g_cfg->admin_context.kind_1_limit > 0 ? g_cfg->admin_context.kind_1_limit : 10;
|
||||||
|
if (limit > 256) {
|
||||||
|
limit = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* dup = strdup(content);
|
||||||
|
if (!dup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
admin_kind1_note_t* grown = (admin_kind1_note_t*)realloc(g_agent_kind1_notes,
|
||||||
|
(size_t)(g_agent_kind1_note_count + 1) * sizeof(admin_kind1_note_t));
|
||||||
|
if (!grown) {
|
||||||
|
free(dup);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_agent_kind1_notes = grown;
|
||||||
|
g_agent_kind1_notes[g_agent_kind1_note_count].created_at = created_at;
|
||||||
|
g_agent_kind1_notes[g_agent_kind1_note_count].content = dup;
|
||||||
|
g_agent_kind1_note_count++;
|
||||||
|
|
||||||
|
while (g_agent_kind1_note_count > limit) {
|
||||||
|
free(g_agent_kind1_notes[0].content);
|
||||||
|
memmove(&g_agent_kind1_notes[0],
|
||||||
|
&g_agent_kind1_notes[1],
|
||||||
|
(size_t)(g_agent_kind1_note_count - 1) * sizeof(admin_kind1_note_t));
|
||||||
|
g_agent_kind1_note_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void on_admin_context_event(cJSON* event, const char* relay_url, void* user_data) {
|
static void on_admin_context_event(cJSON* event, const char* relay_url, void* user_data) {
|
||||||
(void)relay_url;
|
(void)relay_url;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
@@ -1287,6 +1452,51 @@ static void on_admin_context_event(cJSON* event, const char* relay_url, void* us
|
|||||||
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_agent_context_event(cJSON* event, const char* relay_url, void* user_data) {
|
||||||
|
(void)relay_url;
|
||||||
|
(void)user_data;
|
||||||
|
|
||||||
|
if (!event || !g_cfg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_cfg->security.verify_signatures && nostr_verify_event_signature(event) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* kind = cJSON_GetObjectItemCaseSensitive(event, "kind");
|
||||||
|
cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(event, "pubkey");
|
||||||
|
cJSON* content = cJSON_GetObjectItemCaseSensitive(event, "content");
|
||||||
|
cJSON* tags = cJSON_GetObjectItemCaseSensitive(event, "tags");
|
||||||
|
cJSON* created_at = cJSON_GetObjectItemCaseSensitive(event, "created_at");
|
||||||
|
|
||||||
|
if (!kind || !pubkey || !cJSON_IsNumber(kind) || !cJSON_IsString(pubkey) || !pubkey->valuestring) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(pubkey->valuestring, g_cfg->keys.public_key_hex) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int k = (int)kind->valuedouble;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&g_admin_ctx_mutex);
|
||||||
|
|
||||||
|
if (k == 0 && content && cJSON_IsString(content) && content->valuestring) {
|
||||||
|
free(g_agent_kind0_json);
|
||||||
|
g_agent_kind0_json = strdup(content->valuestring);
|
||||||
|
} else if (k == 3 && tags && cJSON_IsArray(tags)) {
|
||||||
|
(void)parse_kind3_agent_contacts(tags);
|
||||||
|
} else if (k == 10002 && tags && cJSON_IsArray(tags)) {
|
||||||
|
(void)parse_kind10002_agent_relays(tags);
|
||||||
|
} else if (k == 1 && content && cJSON_IsString(content) && content->valuestring) {
|
||||||
|
time_t ts = (created_at && cJSON_IsNumber(created_at)) ? (time_t)created_at->valuedouble : time(NULL);
|
||||||
|
upsert_agent_kind1_note(ts, content->valuestring);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static void on_self_skill_event(cJSON* event, const char* relay_url, void* user_data) {
|
static void on_self_skill_event(cJSON* event, const char* relay_url, void* user_data) {
|
||||||
(void)relay_url;
|
(void)relay_url;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
@@ -1469,6 +1679,96 @@ int nostr_handler_subscribe_admin_context(void) {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nostr_handler_subscribe_agent_context(void) {
|
||||||
|
if (!g_cfg || !g_pool) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
cJSON* profile_filter = cJSON_CreateObject();
|
||||||
|
cJSON* profile_kinds = cJSON_CreateArray();
|
||||||
|
cJSON* profile_authors = cJSON_CreateArray();
|
||||||
|
if (!profile_filter || !profile_kinds || !profile_authors) {
|
||||||
|
cJSON_Delete(profile_filter);
|
||||||
|
cJSON_Delete(profile_kinds);
|
||||||
|
cJSON_Delete(profile_authors);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(profile_kinds, cJSON_CreateNumber(0));
|
||||||
|
cJSON_AddItemToArray(profile_kinds, cJSON_CreateNumber(3));
|
||||||
|
cJSON_AddItemToArray(profile_kinds, cJSON_CreateNumber(10002));
|
||||||
|
cJSON_AddItemToObject(profile_filter, "kinds", profile_kinds);
|
||||||
|
cJSON_AddItemToArray(profile_authors, cJSON_CreateString(g_cfg->keys.public_key_hex));
|
||||||
|
cJSON_AddItemToObject(profile_filter, "authors", profile_authors);
|
||||||
|
cJSON_AddNumberToObject(profile_filter, "limit", 64);
|
||||||
|
|
||||||
|
nostr_pool_subscription_t* profile_sub = nostr_relay_pool_subscribe(
|
||||||
|
g_pool,
|
||||||
|
(const char**)g_cfg->relays,
|
||||||
|
g_cfg->relay_count,
|
||||||
|
profile_filter,
|
||||||
|
on_agent_context_event,
|
||||||
|
on_eose,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
NOSTR_POOL_EOSE_FULL_SET,
|
||||||
|
30,
|
||||||
|
120);
|
||||||
|
|
||||||
|
cJSON_Delete(profile_filter);
|
||||||
|
if (!profile_sub) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* notes_filter = cJSON_CreateObject();
|
||||||
|
cJSON* notes_kinds = cJSON_CreateArray();
|
||||||
|
cJSON* notes_authors = cJSON_CreateArray();
|
||||||
|
if (!notes_filter || !notes_kinds || !notes_authors) {
|
||||||
|
cJSON_Delete(notes_filter);
|
||||||
|
cJSON_Delete(notes_kinds);
|
||||||
|
cJSON_Delete(notes_authors);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kind1_limit = g_cfg->admin_context.kind_1_limit > 0 ? g_cfg->admin_context.kind_1_limit : 10;
|
||||||
|
if (kind1_limit > 256) {
|
||||||
|
kind1_limit = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(notes_kinds, cJSON_CreateNumber(1));
|
||||||
|
cJSON_AddItemToObject(notes_filter, "kinds", notes_kinds);
|
||||||
|
cJSON_AddItemToArray(notes_authors, cJSON_CreateString(g_cfg->keys.public_key_hex));
|
||||||
|
cJSON_AddItemToObject(notes_filter, "authors", notes_authors);
|
||||||
|
cJSON_AddNumberToObject(notes_filter, "limit", kind1_limit);
|
||||||
|
|
||||||
|
nostr_pool_subscription_t* notes_sub = nostr_relay_pool_subscribe(
|
||||||
|
g_pool,
|
||||||
|
(const char**)g_cfg->relays,
|
||||||
|
g_cfg->relay_count,
|
||||||
|
notes_filter,
|
||||||
|
on_agent_context_event,
|
||||||
|
on_eose,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
NOSTR_POOL_EOSE_FULL_SET,
|
||||||
|
30,
|
||||||
|
120);
|
||||||
|
|
||||||
|
cJSON_Delete(notes_filter);
|
||||||
|
if (!notes_sub) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
DEBUG_INFO("[didactyl] agent self-context subscriptions active for pubkey %.16s...", g_cfg->keys.public_key_hex);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
void nostr_handler_set_trigger_manager(struct trigger_manager* trigger_manager) {
|
void nostr_handler_set_trigger_manager(struct trigger_manager* trigger_manager) {
|
||||||
g_trigger_manager = trigger_manager;
|
g_trigger_manager = trigger_manager;
|
||||||
}
|
}
|
||||||
@@ -2603,6 +2903,89 @@ char* nostr_handler_get_admin_kind1_notes_context(void) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* nostr_handler_get_agent_kind0_context(void) {
|
||||||
|
if (!g_cfg) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&g_admin_ctx_mutex);
|
||||||
|
char* out = g_agent_kind0_json ? strdup(g_agent_kind0_json) : NULL;
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* nostr_handler_get_agent_kind3_context(void) {
|
||||||
|
if (!g_cfg) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&g_admin_ctx_mutex);
|
||||||
|
|
||||||
|
cJSON* out = cJSON_CreateArray();
|
||||||
|
if (!out) {
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < g_agent_contact_count; i++) {
|
||||||
|
const char* pk = g_agent_contacts[i];
|
||||||
|
if (!pk || pk[0] == '\0') continue;
|
||||||
|
cJSON_AddItemToArray(out, cJSON_CreateString(pk));
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
|
||||||
|
char* out_json = cJSON_PrintUnformatted(out);
|
||||||
|
cJSON_Delete(out);
|
||||||
|
return out_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* nostr_handler_get_agent_kind10002_context(void) {
|
||||||
|
if (!g_cfg) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&g_admin_ctx_mutex);
|
||||||
|
char* out = g_agent_kind10002_json ? strdup(g_agent_kind10002_json) : NULL;
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* nostr_handler_get_agent_kind1_notes_context(void) {
|
||||||
|
if (!g_cfg) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&g_admin_ctx_mutex);
|
||||||
|
|
||||||
|
if (g_agent_kind1_note_count <= 0 || !g_agent_kind1_notes) {
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total = strlen("Agent recent public notes:\n") + 1U;
|
||||||
|
for (int i = 0; i < g_agent_kind1_note_count; i++) {
|
||||||
|
total += strlen("- ") + strlen(g_agent_kind1_notes[i].content ? g_agent_kind1_notes[i].content : "") + 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* out = (char*)malloc(total);
|
||||||
|
if (!out) {
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
out[0] = '\0';
|
||||||
|
strcat(out, "Agent recent public notes:\n");
|
||||||
|
for (int i = 0; i < g_agent_kind1_note_count; i++) {
|
||||||
|
strcat(out, "- ");
|
||||||
|
strcat(out, g_agent_kind1_notes[i].content ? g_agent_kind1_notes[i].content : "");
|
||||||
|
strcat(out, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
int nostr_handler_is_wot_contact(const char* pubkey_hex) {
|
int nostr_handler_is_wot_contact(const char* pubkey_hex) {
|
||||||
if (!pubkey_hex || strlen(pubkey_hex) != 64U) {
|
if (!pubkey_hex || strlen(pubkey_hex) != 64U) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2695,6 +3078,7 @@ void nostr_handler_cleanup(void) {
|
|||||||
|
|
||||||
pthread_mutex_lock(&g_admin_ctx_mutex);
|
pthread_mutex_lock(&g_admin_ctx_mutex);
|
||||||
free_admin_context_locked();
|
free_admin_context_locked();
|
||||||
|
free_agent_context_locked();
|
||||||
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
pthread_mutex_unlock(&g_admin_ctx_mutex);
|
||||||
|
|
||||||
pthread_mutex_lock(&g_self_skill_mutex);
|
pthread_mutex_lock(&g_self_skill_mutex);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ typedef void (*nostr_self_skill_eose_cb_t)(int event_count, void* user_data);
|
|||||||
int nostr_handler_init(didactyl_config_t* config);
|
int nostr_handler_init(didactyl_config_t* config);
|
||||||
void nostr_handler_set_trigger_manager(struct trigger_manager* trigger_manager);
|
void nostr_handler_set_trigger_manager(struct trigger_manager* trigger_manager);
|
||||||
int nostr_handler_subscribe_admin_context(void);
|
int nostr_handler_subscribe_admin_context(void);
|
||||||
|
int nostr_handler_subscribe_agent_context(void);
|
||||||
int nostr_handler_subscribe_self_skills(void);
|
int nostr_handler_subscribe_self_skills(void);
|
||||||
char* nostr_handler_get_self_events_by_kind_json(int kind);
|
char* nostr_handler_get_self_events_by_kind_json(int kind);
|
||||||
void nostr_handler_set_self_skill_eose_callback(nostr_self_skill_eose_cb_t callback, void* user_data);
|
void nostr_handler_set_self_skill_eose_callback(nostr_self_skill_eose_cb_t callback, void* user_data);
|
||||||
@@ -66,6 +67,10 @@ char* nostr_handler_get_admin_kind0_context(void);
|
|||||||
char* nostr_handler_get_admin_kind3_context(void);
|
char* nostr_handler_get_admin_kind3_context(void);
|
||||||
char* nostr_handler_get_admin_kind10002_context(void);
|
char* nostr_handler_get_admin_kind10002_context(void);
|
||||||
char* nostr_handler_get_admin_kind1_notes_context(void);
|
char* nostr_handler_get_admin_kind1_notes_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind0_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind3_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind10002_context(void);
|
||||||
|
char* nostr_handler_get_agent_kind1_notes_context(void);
|
||||||
int nostr_handler_is_wot_contact(const char* pubkey_hex);
|
int nostr_handler_is_wot_contact(const char* pubkey_hex);
|
||||||
char* nostr_handler_relay_status_json(void);
|
char* nostr_handler_relay_status_json(void);
|
||||||
char* nostr_handler_relay_info_json(const char* relay_url);
|
char* nostr_handler_relay_info_json(const char* relay_url);
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
#include "tools_internal.h"
|
#include "tools_internal.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "cjson/cJSON.h"
|
#include "cjson/cJSON.h"
|
||||||
#include "../main.h"
|
#include "../main.h"
|
||||||
|
#include "../nostr_handler.h"
|
||||||
#include "../../nostr_core_lib/nostr_core/nostr_core.h"
|
#include "../../nostr_core_lib/nostr_core/nostr_core.h"
|
||||||
|
|
||||||
static char* json_error_local(const char* msg) {
|
static char* json_error_local(const char* msg) {
|
||||||
@@ -76,6 +78,162 @@ char* execute_agent_identity(tools_context_t* ctx, const char* args_json) {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* execute_nostr_agent_profile(tools_context_t* ctx, const char* args_json) {
|
||||||
|
if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");
|
||||||
|
|
||||||
|
cJSON* args = parse_args_local(args_json);
|
||||||
|
if (!args) return json_error_local("invalid arguments JSON");
|
||||||
|
cJSON_Delete(args);
|
||||||
|
|
||||||
|
char* kind0 = nostr_handler_get_agent_kind0_context();
|
||||||
|
const char* profile_json = (kind0 && kind0[0]) ? kind0 : "{}";
|
||||||
|
|
||||||
|
cJSON* out = cJSON_CreateObject();
|
||||||
|
if (!out) {
|
||||||
|
free(kind0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddBoolToObject(out, "success", 1);
|
||||||
|
cJSON_AddStringToObject(out, "agent_kind0_json", profile_json);
|
||||||
|
|
||||||
|
size_t content_len = strlen("## Agent Kind 0 Profile (source: nostr kind 0)\n\nAgent kind 0 profile content (JSON): ") + strlen(profile_json) + 1U;
|
||||||
|
char* content = (char*)malloc(content_len);
|
||||||
|
if (!content) {
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(kind0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
snprintf(content,
|
||||||
|
content_len,
|
||||||
|
"## Agent Kind 0 Profile (source: nostr kind 0)\n\nAgent kind 0 profile content (JSON): %s",
|
||||||
|
profile_json);
|
||||||
|
cJSON_AddStringToObject(out, "content", content);
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
char* json = cJSON_PrintUnformatted(out);
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(kind0);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* execute_nostr_agent_contacts(tools_context_t* ctx, const char* args_json) {
|
||||||
|
if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");
|
||||||
|
|
||||||
|
cJSON* args = parse_args_local(args_json);
|
||||||
|
if (!args) return json_error_local("invalid arguments JSON");
|
||||||
|
cJSON_Delete(args);
|
||||||
|
|
||||||
|
char* kind3 = nostr_handler_get_agent_kind3_context();
|
||||||
|
const char* contacts_json = (kind3 && kind3[0]) ? kind3 : "[]";
|
||||||
|
|
||||||
|
cJSON* out = cJSON_CreateObject();
|
||||||
|
if (!out) {
|
||||||
|
free(kind3);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddBoolToObject(out, "success", 1);
|
||||||
|
cJSON_AddStringToObject(out, "agent_kind3_json", contacts_json);
|
||||||
|
|
||||||
|
size_t content_len = strlen("## Agent Kind 3 Contacts (source: nostr kind 3)\n\nAgent contacts (JSON): ") + strlen(contacts_json) + 1U;
|
||||||
|
char* content = (char*)malloc(content_len);
|
||||||
|
if (!content) {
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(kind3);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
snprintf(content,
|
||||||
|
content_len,
|
||||||
|
"## Agent Kind 3 Contacts (source: nostr kind 3)\n\nAgent contacts (JSON): %s",
|
||||||
|
contacts_json);
|
||||||
|
cJSON_AddStringToObject(out, "content", content);
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
char* json = cJSON_PrintUnformatted(out);
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(kind3);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* execute_nostr_agent_relays(tools_context_t* ctx, const char* args_json) {
|
||||||
|
if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");
|
||||||
|
|
||||||
|
cJSON* args = parse_args_local(args_json);
|
||||||
|
if (!args) return json_error_local("invalid arguments JSON");
|
||||||
|
cJSON_Delete(args);
|
||||||
|
|
||||||
|
char* kind10002 = nostr_handler_get_agent_kind10002_context();
|
||||||
|
const char* relays_json = (kind10002 && kind10002[0]) ? kind10002 : "[]";
|
||||||
|
|
||||||
|
cJSON* out = cJSON_CreateObject();
|
||||||
|
if (!out) {
|
||||||
|
free(kind10002);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddBoolToObject(out, "success", 1);
|
||||||
|
cJSON_AddStringToObject(out, "agent_kind10002_json", relays_json);
|
||||||
|
|
||||||
|
size_t content_len = strlen("## Agent Kind 10002 Relays (source: nostr kind 10002)\n\nAgent relay list (JSON): ") + strlen(relays_json) + 1U;
|
||||||
|
char* content = (char*)malloc(content_len);
|
||||||
|
if (!content) {
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(kind10002);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
snprintf(content,
|
||||||
|
content_len,
|
||||||
|
"## Agent Kind 10002 Relays (source: nostr kind 10002)\n\nAgent relay list (JSON): %s",
|
||||||
|
relays_json);
|
||||||
|
cJSON_AddStringToObject(out, "content", content);
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
char* json = cJSON_PrintUnformatted(out);
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(kind10002);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* execute_nostr_agent_notes(tools_context_t* ctx, const char* args_json) {
|
||||||
|
if (!ctx || !ctx->cfg) return json_error_local("tool context unavailable");
|
||||||
|
|
||||||
|
cJSON* args = parse_args_local(args_json);
|
||||||
|
if (!args) return json_error_local("invalid arguments JSON");
|
||||||
|
cJSON_Delete(args);
|
||||||
|
|
||||||
|
char* notes = nostr_handler_get_agent_kind1_notes_context();
|
||||||
|
const char* notes_text = (notes && notes[0]) ? notes : "";
|
||||||
|
|
||||||
|
cJSON* out = cJSON_CreateObject();
|
||||||
|
if (!out) {
|
||||||
|
free(notes);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddBoolToObject(out, "success", 1);
|
||||||
|
cJSON_AddStringToObject(out, "agent_notes_content", notes_text);
|
||||||
|
|
||||||
|
size_t content_len = strlen("## Agent Recent Kind 1 Notes\n\n") + strlen(notes_text) + 1U;
|
||||||
|
char* content = (char*)malloc(content_len);
|
||||||
|
if (!content) {
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(notes);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
snprintf(content,
|
||||||
|
content_len,
|
||||||
|
"## Agent Recent Kind 1 Notes\n\n%s",
|
||||||
|
notes_text);
|
||||||
|
cJSON_AddStringToObject(out, "content", content);
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
char* json = cJSON_PrintUnformatted(out);
|
||||||
|
cJSON_Delete(out);
|
||||||
|
free(notes);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
char* execute_agent_version(const char* args_json) {
|
char* execute_agent_version(const char* args_json) {
|
||||||
cJSON* args = parse_args_local(args_json);
|
cJSON* args = parse_args_local(args_json);
|
||||||
if (!args) return json_error_local("invalid arguments JSON");
|
if (!args) return json_error_local("invalid arguments JSON");
|
||||||
|
|||||||
+13
-3
@@ -194,9 +194,19 @@ char* execute_local_http_fetch(tools_context_t* ctx, const char* args_json) {
|
|||||||
return json_error_local("local_http_fetch GET requests cannot include body");
|
return json_error_local("local_http_fetch GET requests cannot include body");
|
||||||
}
|
}
|
||||||
|
|
||||||
int timeout_seconds = (timeout && cJSON_IsNumber(timeout)) ? (int)timeout->valuedouble : 20;
|
int default_timeout = ctx->cfg->tools.local_http_fetch_default_timeout_seconds > 0
|
||||||
if (timeout_seconds <= 0) timeout_seconds = 20;
|
? ctx->cfg->tools.local_http_fetch_default_timeout_seconds
|
||||||
if (timeout_seconds > 120) timeout_seconds = 120;
|
: 20;
|
||||||
|
int max_timeout = ctx->cfg->tools.local_http_fetch_max_timeout_seconds > 0
|
||||||
|
? ctx->cfg->tools.local_http_fetch_max_timeout_seconds
|
||||||
|
: 120;
|
||||||
|
if (default_timeout > max_timeout) {
|
||||||
|
default_timeout = max_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
int timeout_seconds = (timeout && cJSON_IsNumber(timeout)) ? (int)timeout->valuedouble : default_timeout;
|
||||||
|
if (timeout_seconds <= 0) timeout_seconds = default_timeout;
|
||||||
|
if (timeout_seconds > max_timeout) timeout_seconds = max_timeout;
|
||||||
|
|
||||||
int hard_max = ctx->cfg->tools.shell.max_output_bytes > 0 ? ctx->cfg->tools.shell.max_output_bytes : 65536;
|
int hard_max = ctx->cfg->tools.shell.max_output_bytes > 0 ? ctx->cfg->tools.shell.max_output_bytes : 65536;
|
||||||
int max_bytes = (maxb && cJSON_IsNumber(maxb)) ? (int)maxb->valuedouble : hard_max;
|
int max_bytes = (maxb && cJSON_IsNumber(maxb)) ? (int)maxb->valuedouble : hard_max;
|
||||||
|
|||||||
@@ -174,6 +174,30 @@ char* tools_execute_legacy(tools_context_t* ctx, const char* tool_name, const ch
|
|||||||
if (strcmp(tool_name, "agent_identity") == 0) {
|
if (strcmp(tool_name, "agent_identity") == 0) {
|
||||||
return execute_agent_identity(ctx, args_json);
|
return execute_agent_identity(ctx, args_json);
|
||||||
}
|
}
|
||||||
|
if (strcmp(tool_name, "nostr_agent_profile") == 0) {
|
||||||
|
return execute_nostr_agent_profile(ctx, args_json);
|
||||||
|
}
|
||||||
|
if (strcmp(tool_name, "nostr_agent_contacts") == 0) {
|
||||||
|
return execute_nostr_agent_contacts(ctx, args_json);
|
||||||
|
}
|
||||||
|
if (strcmp(tool_name, "nostr_agent_relays") == 0) {
|
||||||
|
return execute_nostr_agent_relays(ctx, args_json);
|
||||||
|
}
|
||||||
|
if (strcmp(tool_name, "nostr_agent_notes") == 0) {
|
||||||
|
return execute_nostr_agent_notes(ctx, args_json);
|
||||||
|
}
|
||||||
|
if (strcmp(tool_name, "my_kind0_profile") == 0) {
|
||||||
|
return execute_nostr_agent_profile(ctx, args_json);
|
||||||
|
}
|
||||||
|
if (strcmp(tool_name, "my_contacts") == 0) {
|
||||||
|
return execute_nostr_agent_contacts(ctx, args_json);
|
||||||
|
}
|
||||||
|
if (strcmp(tool_name, "my_relays") == 0) {
|
||||||
|
return execute_nostr_agent_relays(ctx, args_json);
|
||||||
|
}
|
||||||
|
if (strcmp(tool_name, "my_notes") == 0) {
|
||||||
|
return execute_nostr_agent_notes(ctx, args_json);
|
||||||
|
}
|
||||||
|
|
||||||
return json_error("unknown tool");
|
return json_error("unknown tool");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ char* execute_trigger_list(tools_context_t* ctx, const char* args_json);
|
|||||||
char* execute_message_current(tools_context_t* ctx, const char* args_json);
|
char* execute_message_current(tools_context_t* ctx, const char* args_json);
|
||||||
char* execute_agent_identity(tools_context_t* ctx, const char* args_json);
|
char* execute_agent_identity(tools_context_t* ctx, const char* args_json);
|
||||||
char* execute_agent_version(const char* args_json);
|
char* execute_agent_version(const char* args_json);
|
||||||
|
char* execute_nostr_agent_profile(tools_context_t* ctx, const char* args_json);
|
||||||
|
char* execute_nostr_agent_contacts(tools_context_t* ctx, const char* args_json);
|
||||||
|
char* execute_nostr_agent_relays(tools_context_t* ctx, const char* args_json);
|
||||||
|
char* execute_nostr_agent_notes(tools_context_t* ctx, const char* args_json);
|
||||||
char* execute_model_get(const char* args_json);
|
char* execute_model_get(const char* args_json);
|
||||||
char* execute_model_set(tools_context_t* ctx, const char* args_json);
|
char* execute_model_set(tools_context_t* ctx, const char* args_json);
|
||||||
char* execute_model_list(const char* args_json);
|
char* execute_model_list(const char* args_json);
|
||||||
|
|||||||
@@ -1066,6 +1066,118 @@ char* tools_build_openai_schema_json_legacy(const tools_context_t* ctx) {
|
|||||||
cJSON_AddItemToObject(t41, "function", t41_fn);
|
cJSON_AddItemToObject(t41, "function", t41_fn);
|
||||||
cJSON_AddItemToArray(tools, t41);
|
cJSON_AddItemToArray(tools, t41);
|
||||||
|
|
||||||
|
cJSON* t41b = cJSON_CreateObject();
|
||||||
|
cJSON* t41b_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41b_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41b_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41b, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41b_fn, "name", "nostr_agent_profile");
|
||||||
|
cJSON_AddStringToObject(t41b_fn, "description", "Build agent profile context block from cached kind 0 metadata");
|
||||||
|
cJSON_AddStringToObject(t41b_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41b_params, "properties", t41b_props);
|
||||||
|
cJSON_AddItemToObject(t41b_fn, "parameters", t41b_params);
|
||||||
|
cJSON_AddItemToObject(t41b, "function", t41b_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41b);
|
||||||
|
|
||||||
|
cJSON* t41c = cJSON_CreateObject();
|
||||||
|
cJSON* t41c_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41c_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41c_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41c, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41c_fn, "name", "nostr_agent_contacts");
|
||||||
|
cJSON_AddStringToObject(t41c_fn, "description", "Build agent contacts context block from cached kind 3 contact list");
|
||||||
|
cJSON_AddStringToObject(t41c_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41c_params, "properties", t41c_props);
|
||||||
|
cJSON_AddItemToObject(t41c_fn, "parameters", t41c_params);
|
||||||
|
cJSON_AddItemToObject(t41c, "function", t41c_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41c);
|
||||||
|
|
||||||
|
cJSON* t41d = cJSON_CreateObject();
|
||||||
|
cJSON* t41d_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41d_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41d_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41d, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41d_fn, "name", "nostr_agent_relays");
|
||||||
|
cJSON_AddStringToObject(t41d_fn, "description", "Build agent relay context block from cached kind 10002 data");
|
||||||
|
cJSON_AddStringToObject(t41d_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41d_params, "properties", t41d_props);
|
||||||
|
cJSON_AddItemToObject(t41d_fn, "parameters", t41d_params);
|
||||||
|
cJSON_AddItemToObject(t41d, "function", t41d_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41d);
|
||||||
|
|
||||||
|
cJSON* t41e = cJSON_CreateObject();
|
||||||
|
cJSON* t41e_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41e_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41e_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41e, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41e_fn, "name", "nostr_agent_notes");
|
||||||
|
cJSON_AddStringToObject(t41e_fn, "description", "Build agent notes context block from cached kind 1 notes");
|
||||||
|
cJSON_AddStringToObject(t41e_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41e_params, "properties", t41e_props);
|
||||||
|
cJSON_AddItemToObject(t41e_fn, "parameters", t41e_params);
|
||||||
|
cJSON_AddItemToObject(t41e, "function", t41e_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41e);
|
||||||
|
|
||||||
|
cJSON* t41f = cJSON_CreateObject();
|
||||||
|
cJSON* t41f_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41f_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41f_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41f, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41f_fn, "name", "my_kind0_profile");
|
||||||
|
cJSON_AddStringToObject(t41f_fn, "description", "Alias for nostr_agent_profile: return this agent's kind 0 profile context");
|
||||||
|
cJSON_AddStringToObject(t41f_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41f_params, "properties", t41f_props);
|
||||||
|
cJSON_AddItemToObject(t41f_fn, "parameters", t41f_params);
|
||||||
|
cJSON_AddItemToObject(t41f, "function", t41f_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41f);
|
||||||
|
|
||||||
|
cJSON* t41g = cJSON_CreateObject();
|
||||||
|
cJSON* t41g_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41g_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41g_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41g, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41g_fn, "name", "my_contacts");
|
||||||
|
cJSON_AddStringToObject(t41g_fn, "description", "Alias for nostr_agent_contacts: return this agent's kind 3 contacts context");
|
||||||
|
cJSON_AddStringToObject(t41g_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41g_params, "properties", t41g_props);
|
||||||
|
cJSON_AddItemToObject(t41g_fn, "parameters", t41g_params);
|
||||||
|
cJSON_AddItemToObject(t41g, "function", t41g_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41g);
|
||||||
|
|
||||||
|
cJSON* t41h = cJSON_CreateObject();
|
||||||
|
cJSON* t41h_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41h_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41h_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41h, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41h_fn, "name", "my_relays");
|
||||||
|
cJSON_AddStringToObject(t41h_fn, "description", "Alias for nostr_agent_relays: return this agent's kind 10002 relay context");
|
||||||
|
cJSON_AddStringToObject(t41h_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41h_params, "properties", t41h_props);
|
||||||
|
cJSON_AddItemToObject(t41h_fn, "parameters", t41h_params);
|
||||||
|
cJSON_AddItemToObject(t41h, "function", t41h_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41h);
|
||||||
|
|
||||||
|
cJSON* t41i = cJSON_CreateObject();
|
||||||
|
cJSON* t41i_fn = cJSON_CreateObject();
|
||||||
|
cJSON* t41i_params = cJSON_CreateObject();
|
||||||
|
cJSON* t41i_props = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(t41i, "type", "function");
|
||||||
|
cJSON_AddStringToObject(t41i_fn, "name", "my_notes");
|
||||||
|
cJSON_AddStringToObject(t41i_fn, "description", "Alias for nostr_agent_notes: return this agent's recent kind 1 notes context");
|
||||||
|
cJSON_AddStringToObject(t41i_params, "type", "object");
|
||||||
|
cJSON_AddItemToObject(t41i_params, "properties", t41i_props);
|
||||||
|
cJSON_AddItemToObject(t41i_fn, "parameters", t41i_params);
|
||||||
|
cJSON_AddItemToObject(t41i, "function", t41i_fn);
|
||||||
|
cJSON_AddItemToArray(tools, t41i);
|
||||||
|
|
||||||
cJSON* t42 = cJSON_CreateObject();
|
cJSON* t42 = cJSON_CreateObject();
|
||||||
cJSON* t42_fn = cJSON_CreateObject();
|
cJSON* t42_fn = cJSON_CreateObject();
|
||||||
cJSON* t42_params = cJSON_CreateObject();
|
cJSON* t42_params = cJSON_CreateObject();
|
||||||
|
|||||||
Reference in New Issue
Block a user