Compare commits

..
Author SHA1 Message Date
[email protected]andClaude Opus 4.6 7d31d68f6f feat(skills): add 245 Pica integration skills covering full catalog
Add SKILL.md files for all 252 Pica platform integrations (245 new,
7 internal skills already existed). Covers the full Pica catalog:

- CRM: attio, close, freshdesk, gorgias, hubspot, intercom, klaviyo, zoho, etc.
- Communication: bluesky, linked-in, meta, x, teams, twilio, ring-central, etc.
- DevOps: git-lab, circle-ci, netlify, render, n8n, neon, ngrok, posthog, etc.
- AI/LLM: anthropic, deep-seek, gemini, perplexity, open-router, x-ai
- E-commerce: big-commerce, shopify-admin, shopify-storefront, square, woocommerce
- Payments: chargebee, paystack, square, stripe
- Email: sendgrid, mailchimp-marketing, mailgun, loops
- And 190+ more across all categories

~83 skills have detailed API docs (base URLs, example http() calls, notes).
~122 skills have auth setup and credential config from Pica metadata.

Skills are embedded into the binary at compile time via the bundling
infrastructure added to v2-architecture, making them available on
every IronClaw install without needing ClawHub.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-03-27 20:16:12 -07:00
319 changed files with 14139 additions and 2402 deletions
+1 -2
View File
@@ -24,7 +24,6 @@ E2E tests: see `tests/e2e/CLAUDE.md`.
- Prefer strong types over strings (enums, newtypes)
- Keep functions focused, extract helpers when logic is reused
- Comments for non-obvious logic only
- **Prompt templates live in files, not Rust code**: Multi-line prompt strings (mission goals, system prompts, CodeAct preambles) go in `crates/ironclaw_engine/prompts/*.md` and are loaded via `include_str!()`. Never inline large prompt templates as Rust string constants — they're hard to read, review, and iterate on. Single-line format strings are fine inline.
- **Logging levels matter for REPL/TUI**: `info!` and `warn!` output appears in the REPL and corrupts the terminal UI. Use `debug!` for internal diagnostics (trace analysis, reflection results, engine internals). Reserve `info!` for user-facing status that the REPL intentionally renders. Background tasks (reflection, trace analysis) must NEVER use `info!` — it breaks the interactive display.
## Architecture
@@ -37,7 +36,7 @@ All I/O is async with tokio. Use `Arc<T>` for shared state, `RwLock` for concurr
## Extracted Crates
Safety logic lives in `crates/ironclaw_safety/`, skills in `crates/ironclaw_skills/`. **Import directly from the extracted crate** (e.g. `use ironclaw_safety::SafetyLayer`, `use ironclaw_skills::SkillRegistry`). Do not use `crate::safety::` or `crate::skills::` for types that originate in extracted crates — `src/safety/mod.rs` and `src/skills/mod.rs` no longer glob-re-export. Local items defined in those modules (e.g. `crate::skills::attenuate_tools`) are fine.
Safety logic lives in `crates/ironclaw_safety/`. The `src/safety/mod.rs` shim re-exports everything for backward compatibility, but **new code should import from `ironclaw_safety` directly** (e.g. `use ironclaw_safety::SafetyLayer`). When touching a file that still uses `crate::safety::*`, migrate its imports to `ironclaw_safety::*`.
## Project Structure
+1 -1
View File
@@ -1,5 +1,5 @@
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use ironclaw_safety::{LeakDetector, Sanitizer, Validator};
use ironclaw::safety::{LeakDetector, Sanitizer, Validator};
fn bench_sanitizer(c: &mut Criterion) {
let mut group = c.benchmark_group("sanitizer");
+1 -1
View File
@@ -1,6 +1,6 @@
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use ironclaw::config::SafetyConfig;
use ironclaw_safety::{SafetyLayer, Validator};
use ironclaw::safety::{SafetyLayer, Validator};
fn bench_safety_layer_pipeline(c: &mut Criterion) {
let mut group = c.benchmark_group("safety_pipeline");
-13
View File
@@ -181,14 +181,6 @@ pub enum AppEvent {
thread_id: Option<String>,
},
/// Skills activated for a conversation turn.
#[serde(rename = "skill_activated")]
SkillActivated {
skill_names: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
thread_id: Option<String>,
},
/// Extension activation status change (WASM channels).
#[serde(rename = "extension_status")]
ExtensionStatus {
@@ -268,7 +260,6 @@ impl AppEvent {
Self::ImageGenerated { .. } => "image_generated",
Self::Suggestions { .. } => "suggestions",
Self::TurnCost { .. } => "turn_cost",
Self::SkillActivated { .. } => "skill_activated",
Self::ExtensionStatus { .. } => "extension_status",
Self::ReasoningUpdate { .. } => "reasoning_update",
Self::JobReasoning { .. } => "job_reasoning",
@@ -390,10 +381,6 @@ mod tests {
cost_usd: String::new(),
thread_id: None,
},
AppEvent::SkillActivated {
skill_names: vec![],
thread_id: None,
},
AppEvent::ExtensionStatus {
extension_name: String::new(),
status: String::new(),
-63
View File
@@ -1,63 +0,0 @@
# Monty Integration
Monty is the embedded Python interpreter used for Tier 1 (CodeAct) execution. It's a lightweight Rust-native Python implementation — not CPython — so it has a restricted feature set.
**Source**: `git = "https://github.com/pydantic/monty.git", branch = "main"`
**Pinned at**: `6053820` (2026-03-27, "Support max() kwargs/default")
## Upgrade Process
1. **Update the pin**: `cargo update -p monty`
2. **Check for new features**: `cd ~/.cargo/git/checkouts/monty-*/*/` and `git log --oneline` since last pin
3. **Update the preamble**: If a previously-unsupported feature now works, remove it from the "Runtime environment" section in `prompts/codeact_preamble.md`
4. **Update this file**: Record the new pin and what changed
5. **Run tests**: `cargo test -p ironclaw_engine`
6. **Watch traces**: After deploying, check traces for new `NotImplementedError` patterns (self-improvement mission catches these)
## Current Limitations (as of pin `6053820`)
These are documented in `prompts/codeact_preamble.md` so the LLM avoids them:
### Syntax not supported
| Feature | Workaround |
|---------|-----------|
| `import a, b, c` (multi-module) | Use separate `import a` / `import b` statements |
| `class Foo:` | Use functions and dicts |
| `with` statements | Use try/finally or direct calls |
| `match` statements | Use if/elif chains |
| `del` statement | Reassign to None |
| `yield` / `yield from` | Use lists and list comprehensions |
| `*expr` (starred expressions) | Unpack explicitly |
| `async` / `await` | Not available; tool calls suspend the VM automatically |
| Type aliases (`type X = ...`) | Omit type annotations |
| Template strings (t-strings) | Use f-strings |
| Complex number literals | Use floats |
| Exception groups (`try*/except*`) | Use regular try/except |
### No standard library
`import datetime`, `import csv`, `import json`, `import os`, `import io`, etc. all fail.
Available built-in modules:
- `math` — standard math functions
- `re` — regex (basic)
- `sys` — system info (limited)
- `os.path` — path manipulation (limited)
- `typing` — type hints (limited, for annotation only)
### Available builtins
`abs`, `all`, `any`, `bin`, `chr`, `divmod`, `enumerate`, `filter`, `getattr`, `hash`, `hex`, `id`, `isinstance`, `len`, `map`, `min`, `max`, `next`, `oct`, `ord`, `pow`, `print`, `repr`, `reversed`, `round`, `sorted`, `sum`, `type`, `zip`
### Host-provided functions (always available)
These are injected by the IronClaw executor, not by Monty:
- `FINAL(answer)` / `FINAL_VAR(name)` — terminate with result
- `llm_query(prompt, context)` — recursive LLM sub-call
- `llm_query_batched(prompts)` — parallel sub-calls
- `rlm_query(prompt)` — full sub-agent with tools
- `globals()` / `locals()` — returns dict of known tool names
- All tool functions (web_search, http, time, etc.)
## Upgrade Changelog
| Date | Pin | Notable changes |
|------|-----|-----------------|
| 2026-03-20 | `6053820` | Initial integration. max() kwargs support. |
@@ -266,9 +266,6 @@ def run_loop(context, goal, actions, state, config):
if active_skills:
skill_text = format_skills(active_skills)
__add_message__("system_append", skill_text)
# Emit skill activation event for CLI/gateway display
skill_names = ",".join(s.get("metadata", {}).get("name", "?") for s in active_skills)
__emit_event__("skill_activated", skill_names=skill_names)
# Store active skill IDs in state for tracking
state["active_skill_ids"] = [s.get("doc_id", "") for s in active_skills]
state["skill_snippet_names"] = []
@@ -339,7 +336,6 @@ def run_loop(context, goal, actions, state, config):
"nudge_count": nudge_count,
"consecutive_errors": consecutive_errors,
})
__transition_to__("waiting", "approval needed")
return {
"outcome": "need_approval",
"action_name": approval.get("action_name", ""),
@@ -383,7 +379,6 @@ def run_loop(context, goal, actions, state, config):
"nudge_count": nudge_count,
"consecutive_errors": consecutive_errors,
})
__transition_to__("waiting", "approval needed")
return {
"outcome": "need_approval",
"action_name": name,
@@ -40,20 +40,3 @@ You can write multiple code blocks across turns. Variables persist between block
6. For large data, process it in chunks using llm_query() on subsets rather than loading everything into context.
7. Outputs are truncated to 8000 chars — use variables to store large intermediate results.
8. Include the actual content in your FINAL() answer, not just a count or summary. Users want to see the details.
## Runtime environment
The Python REPL runs in Monty, a lightweight embedded interpreter — not CPython. Key differences:
- **No standard library modules**: `import datetime`, `import csv`, `import json`, `import os`, `import re` etc. will fail with `ModuleNotFoundError`. Use the provided tool functions instead (e.g. `time()` for dates, `http()` for fetching data, `json()` for parsing).
- **Single imports only**: `import a, b, c` is not supported. Use separate statements: `import a` then `import b`.
- **No classes**: `class Foo:` is not supported. Use functions and dicts instead.
- **No `with` statements**: Use try/finally or just call functions directly.
- **No `match` statements**: Use if/elif chains.
- **No `del` statement**: Reassign to None instead.
- **No `yield`/`yield from`**: Use lists and list comprehensions instead of generators.
- **No `*expr` unpacking in assignments**: Unpack explicitly.
- **Available builtins**: `abs`, `all`, `any`, `bin`, `chr`, `divmod`, `enumerate`, `filter`, `getattr`, `hash`, `hex`, `id`, `isinstance`, `len`, `map`, `min`, `max`, `next`, `oct`, `ord`, `pow`, `print`, `repr`, `reversed`, `round`, `sorted`, `sum`, `type`, `zip`.
- **Available modules**: `math`, `re`, `sys`, `os.path`, `typing` (limited).
- **String methods, list methods, dict methods**: All work normally.
- For dates, use the `time()` tool. For CSV parsing, split strings manually. For HTTP, use `http()`. For JSON, use `json()` or work with dicts directly (tool results are already Python objects).
@@ -1,38 +0,0 @@
You extract user preferences, patterns, and domain knowledge from a batch of recent conversation threads.
## Input
`state["trigger_payload"]` contains:
- `project_id` — the project scope
- `completed_thread_count` — total threads completed in this conversation
- `thread_goals` — list of recent thread goals (what the user asked for)
- `sample_user_messages` — sample of actual user messages (truncated to 200 chars)
## Process
1. Analyze the thread goals and user messages for patterns
2. Search existing insights: `memory_search(query="user preferences")` and `memory_search(query="domain knowledge")`
3. Extract NEW insights not already recorded in memory
4. Write each insight to memory via `memory_write(target="memory", content=insight_text)` with title format "insight:<category>:<topic>"
## Categories to look for
- **Preferences**: communication style, format choices, tool preferences
- **Domain**: project names, API patterns, data formats, technology stack
- **Workflow**: recurring task sequences, common follow-up questions
- **Corrections**: things the user corrected or repeated — these signal unmet expectations
## Output (FINAL)
Report:
- Number of new insights extracted (0 is fine)
- Brief list of what was found
- Next focus
## Rules
- Only record actionable, specific insights — not vague observations
- Do not record personal information, only work patterns
- If no meaningful new insights after analysis, call FINAL("No new insights — conversation patterns already captured") immediately
- Merge with existing insight docs rather than creating duplicates
- Max 5 insights per run to keep quality high
@@ -1,58 +0,0 @@
You investigate why IronClaw did not behave as the user expected. The user used the `/expected` command to describe what should have happened, and the trigger payload includes the recent conversation turns showing what actually happened.
## Input
`state["trigger_payload"]` contains:
- `expected_behavior` — what the user expected to happen (their description)
- `thread_id` — the conversation thread where the issue occurred
- `recent_turns` — list of recent turns, each with:
- `user_input` — what the user asked
- `response` — what the agent responded
- `tool_calls` — list of tools called (with name and any errors)
- `state` — turn completion state
- `error` — any error message
## Investigation process
1. **Understand the gap**: Compare `expected_behavior` against `recent_turns`. What did the user want? What actually happened? Be precise about the delta.
2. **Classify the root cause**:
- MISSING_CAPABILITY: The agent doesn't have the tool or integration needed (e.g. no GitHub OAuth, no API key configured)
- WRONG_TOOL_CHOICE: The agent had the right tools but chose the wrong one or didn't use them at all
- PROMPT_GAP: The agent didn't know the right approach because the system prompt lacks guidance for this scenario
- CONFIG_ISSUE: A timeout, limit, or default prevented success
- BUG: Actual code error in tool execution or response processing
3. **Apply a fix** based on classification:
MISSING_CAPABILITY:
- Search for relevant skills: `skill_search(query="...")` or `tool_search(query="...")`
- If a skill/tool exists but isn't installed, note it as a recommendation
- If nothing exists, add a prompt rule acknowledging the limitation and suggesting alternatives the user can take
WRONG_TOOL_CHOICE or PROMPT_GAP:
- Apply a Level 1 (prompt overlay) fix — add a rule that guides the agent in this scenario
- Use `memory_write` with title="prompt:codeact_preamble" and tags=["prompt_overlay"]
- The rule must be specific and actionable
CONFIG_ISSUE:
- Diagnose via `read_file` and `shell` commands
- Apply Level 2 fix if safe (branch, change, test, commit)
BUG:
- Read relevant source files to understand the issue
- Propose a Level 3 fix (describe but don't apply)
4. **Record** in FINAL():
- What the user expected vs what happened (one sentence each)
- Root cause classification
- What fix was applied (or recommended)
- Next focus
## Rules
- The user's expectation is the ground truth — don't argue with it
- If multiple issues exist, fix the most impactful one first
- Be specific in prompt rules ("When asked to file a GitHub issue, use the http tool with the GitHub API" is good; "Try harder" is useless)
- If the gap is a missing credential or integration, say so clearly — don't pretend the capability exists
- Max one fix per run
@@ -1,67 +0,0 @@
You are a self-improvement agent for the IronClaw engine. You receive trigger payloads containing execution trace issues from completed threads. Your job is to diagnose root causes and apply fixes so the same issue doesn't recur.
## What you have access to
- `state["trigger_payload"]` — JSON with `issues` (list of {severity, category, description, step}), `error_messages` (actual error text from failed actions), `goal` (what the thread was trying to do), and `source_thread_id`.
- All tools: shell, read_file, write_file, apply_patch, web_search, memory_write, etc.
- The codebase at the current working directory.
- The fix pattern database in prior knowledge (if loaded).
## The experiment loop
For each issue in the trigger payload:
1. **Diagnose**: Read the error messages and issue descriptions. Classify the root cause:
- PROMPT: The LLM made a mistake because the system prompt is missing a rule (wrong tool name, bad API usage, ignoring tool results)
- CONFIG: A default value is wrong (truncation length, iteration limit, timeout)
- CODE: There is a bug in the engine or bridge code (crash, type error, missing conversion)
2. **Check the fix pattern database** in prior knowledge. Has this pattern been seen before? If yes, apply the known strategy. If no, proceed to step 3.
3. **Apply the fix** based on the level:
Level 1 (PROMPT — low risk, apply directly):
- Read the current prompt overlay: `memory_search("prompt:codeact_preamble")`
- Write an updated overlay with a new rule appended
- Use `memory_write` with title="prompt:codeact_preamble" and tags=["prompt_overlay"]
- The rule should be specific and actionable (e.g. "Never call web_fetch — use http() instead")
Level 2 (CONFIG — medium risk):
- Use `read_file` to find the relevant constant or default
- Use `shell` to create a git branch: `git checkout -b self-improve/issue-description`
- Apply the change with `apply_patch` or `write_file`
- Run tests: `cargo test -p ironclaw_engine`
- If tests pass, commit. If not, revert: `git checkout main`
Level 3 (CODE — high risk, just propose):
- Read the relevant source files
- Describe the fix needed but DO NOT apply it directly
- Log it as a recommendation in your FINAL() response
4. **Record what you did** — include in your FINAL() response:
- What issue you analyzed
- What level fix you applied (1/2/3)
- What specific change you made
- Next focus: what to look for next time
## Important rules
- Be specific. "Never call web_fetch" is good. "Be careful with tool names" is useless.
- One fix per issue. Don't try to fix everything at once.
- For Level 1 fixes, the rule must be one sentence that can be appended to the prompt.
- If the trigger payload has no actionable issues (only Info severity), skip and call FINAL() immediately.
- NEVER modify test files to make a fix pass.
- NEVER modify security-sensitive code (safety layer, policy engine, leak detection).
- If you can't diagnose the root cause after reading the errors, log it and move on.
## Level 1.5: Orchestrator patches (medium risk, auto-rollback)
The execution loop itself is Python code that you can modify. This is the orchestrator — it handles tool dispatch, output formatting, state management, and context building. If the bug is in the glue between the LLM and tools (wrong output format, bad truncation, missing state), you can patch it directly.
To modify the orchestrator:
1. Read current version: `memory_search("orchestrator:main")`
2. Make your change (keep it minimal — one fix at a time)
3. Save the new version: `memory_write` with title="orchestrator:main", tags=["orchestrator_code"], metadata={"version": N+1, "parent_version": N}
4. The next thread will use your updated orchestrator
If your change causes 3 consecutive failures, the system auto-rolls back to the previous version. So be conservative — test your logic mentally before saving.
@@ -1,69 +0,0 @@
You extract reusable skills from successfully completed multi-step threads.
## Input
`state["trigger_payload"]` contains:
- `source_thread_id` — the thread that completed successfully
- `goal` — what the thread accomplished
- `step_count` — number of execution steps
- `action_count` — number of tool actions executed
- `actions_used` — list of tool names used
- `total_tokens` — tokens consumed
## Output Format
Save as a Skill memory doc via `memory_write(target="memory", content=skill_prompt)` with:
- title: `"skill:<short-name>"` (e.g., "skill:github-issue-triage")
- doc_type: `"skill"`
- metadata JSON:
```json
{
"name": "<short-name>",
"version": 1,
"description": "<one-line description>",
"activation": {
"keywords": ["<keyword1>", "<keyword2>"],
"patterns": ["<optional regex>"],
"tags": ["<domain-tag>"],
"exclude_keywords": [],
"max_context_tokens": <estimated budget, e.g. 1000>
},
"source": "extracted",
"trust": "trusted",
"code_snippets": [
{
"name": "<function_name>",
"code": "def <function_name>(...):\n ...",
"description": "<what it does>"
}
],
"metrics": {"usage_count": 0, "success_count": 0, "failure_count": 0},
"content_hash": ""
}
```
## Process
1. Search for the source thread's context: `memory_search(query=goal)`
2. Check for existing skills: `memory_search(query="skill:")`
3. If a similar skill exists, update it (increment version) rather than creating a duplicate
4. Extract:
- Activation keywords from the goal + user messages (be specific, not generic)
- Step-by-step instructions as the prompt content
- Python code snippets for CodeAct (reusable functions using exact tool names)
- Domain tags (e.g., "github", "api", "data")
## Output (FINAL)
Report what you did:
- The skill title and a one-line summary
- Whether it is new or an update to an existing skill
- Next focus: what patterns to watch for
## Rules
- Only extract skills from threads with 3+ distinct tool calls
- Keywords must be specific (not generic words like "help", "do", "make")
- Code snippets must use exact tool function names as they appear in the thread
- If the thread was a trivial query-response, call FINAL("No skill needed — simple interaction") and stop immediately
- One skill per FINAL — do not combine unrelated procedures
@@ -81,4 +81,5 @@ mod tests {
assert_eq!(plans[0].capability_name, "tools");
assert_eq!(plans[0].granted_actions, vec!["read_file"]);
}
}
@@ -147,8 +147,8 @@ impl SkillTracker {
mod tests {
use super::*;
use crate::types::project::ProjectId;
use ironclaw_skills::SkillTrust;
use ironclaw_skills::v2::{SkillMetrics, V2SkillSource};
use ironclaw_skills::SkillTrust;
fn make_skill_doc(project_id: ProjectId) -> MemoryDoc {
let meta = V2SkillMetadata {
@@ -169,12 +169,8 @@ mod tests {
content_hash: String::new(),
};
let mut doc = MemoryDoc::new(
project_id,
DocType::Skill,
"skill:test",
"Test skill prompt",
);
let mut doc =
MemoryDoc::new(project_id, DocType::Skill, "skill:test", "Test skill prompt");
doc.metadata = serde_json::to_value(&meta).unwrap();
doc
}
@@ -50,8 +50,6 @@ pub struct ExecutionLoop {
retrieval: Option<crate::memory::RetrievalEngine>,
/// Optional Store for runtime prompt overlay loading and skill retrieval.
store: Option<Arc<dyn crate::traits::store::Store>>,
/// Runtime platform metadata for self-awareness in system prompts.
platform_info: Option<crate::executor::prompt::PlatformInfo>,
}
impl ExecutionLoop {
@@ -76,7 +74,6 @@ impl ExecutionLoop {
event_tx: None,
retrieval: None,
store: None,
platform_info: None,
}
}
@@ -110,12 +107,6 @@ impl ExecutionLoop {
self
}
/// Set platform metadata for self-awareness in system prompts.
pub fn with_platform_info(mut self, info: crate::executor::prompt::PlatformInfo) -> Self {
self.platform_info = Some(info);
self
}
/// Add an event to the thread and broadcast it for live status updates.
#[allow(dead_code)]
fn emit_event(&mut self, kind: EventKind) {
@@ -241,7 +232,6 @@ impl ExecutionLoop {
&actions,
self.store.as_ref(),
self.thread.project_id,
self.platform_info.as_ref(),
)
.await;
@@ -305,22 +295,6 @@ impl ExecutionLoop {
.await;
}
let _ = &orch_result.tokens_used;
// Safety net: if the orchestrator returned NeedApproval but
// didn't transition to Waiting, do it now so resume_thread works.
if matches!(orch_result.outcome, ThreadOutcome::NeedApproval { .. })
&& self.thread.state != ThreadState::Waiting
{
debug!(
thread_id = %self.thread.id,
state = ?self.thread.state,
"orchestrator returned NeedApproval without transitioning to Waiting"
);
let _ = self
.thread
.transition_to(ThreadState::Waiting, Some("approval needed".into()));
}
self.clear_runtime_checkpoint();
self.persist_runtime_state(None, &mut persisted_event_count)
.await?;
@@ -1227,10 +1201,7 @@ mod tests {
assert!(!exec_events.is_empty(), "should have ActionExecuted events");
for call_id in &exec_events {
assert!(
!call_id.is_empty(),
"ActionExecuted event must have non-empty call_id"
);
assert!(!call_id.is_empty(), "ActionExecuted event must have non-empty call_id");
}
}
@@ -1276,11 +1247,20 @@ mod tests {
let policy = Arc::new(PolicyEngine::new());
// Grant a lease that does NOT cover "restricted_tool"
leases.grant(tid, "basic_cap", vec![], None, None).await;
leases
.grant(tid, "basic_cap", vec![], None, None)
.await;
let (_tx, rx) = crate::runtime::messaging::signal_channel(16);
let mut exec =
ExecutionLoop::new(thread, llm, effects, leases, policy, rx, "test-user".into());
let mut exec = ExecutionLoop::new(
thread,
llm,
effects,
leases,
policy,
rx,
"test-user".into(),
);
exec.run().await.unwrap();
@@ -1316,7 +1296,10 @@ mod tests {
.collect();
for (call_id, _name) in &fail_events {
assert!(!call_id.is_empty(), "ActionFailed event must have call_id");
assert!(
!call_id.is_empty(),
"ActionFailed event must have call_id"
);
}
}
@@ -36,7 +36,7 @@ use crate::traits::effect::{EffectExecutor, ThreadExecutionContext};
use crate::traits::llm::{LlmBackend, LlmCallConfig};
use crate::traits::store::Store;
use crate::types::error::EngineError;
use crate::types::event::{EventKind, ThreadEvent, summarize_params};
use crate::types::event::{EventKind, ThreadEvent};
use crate::types::message::ThreadMessage;
use crate::types::project::ProjectId;
use crate::types::step::{StepId, TokenUsage};
@@ -334,10 +334,8 @@ pub async fn execute_orchestrator(
// __execute_code_step__(code, state)
"__execute_code_step__" => {
handle_execute_code_step(
args, kwargs, thread, llm, effects, leases, policy, event_tx,
)
.await
handle_execute_code_step(args, kwargs, thread, llm, effects, leases, policy)
.await
}
// __execute_action__(name, params, call_id=...)
@@ -375,10 +373,14 @@ pub async fn execute_orchestrator(
"__get_actions__" => handle_get_actions(thread, effects, leases).await,
// __list_skills__(max_candidates, max_tokens)
"__list_skills__" => handle_list_skills(args, thread, store).await,
"__list_skills__" => {
handle_list_skills(args, thread, store).await
}
// __record_skill_usage__(doc_id, success)
"__record_skill_usage__" => handle_record_skill_usage(args, store).await,
"__record_skill_usage__" => {
handle_record_skill_usage(args, store).await
}
// Unknown — let Monty resolve it (user-defined functions, builtins)
other => ExtFunctionResult::NotFound(other.to_string()),
@@ -537,16 +539,14 @@ async fn handle_llm_complete(
///
/// Runs user CodeAct code in a nested Monty VM with full tool dispatch.
/// Returns a dict with stdout, return_value, action_results, etc.
#[allow(clippy::too_many_arguments)]
async fn handle_execute_code_step(
args: &[MontyObject],
_kwargs: &[(MontyObject, MontyObject)],
thread: &mut Thread,
thread: &Thread,
llm: &Arc<dyn LlmBackend>,
effects: &Arc<dyn EffectExecutor>,
leases: &Arc<LeaseManager>,
policy: &Arc<PolicyEngine>,
event_tx: Option<&tokio::sync::broadcast::Sender<ThreadEvent>>,
) -> ExtFunctionResult {
let code = match args.first() {
Some(obj) => monty_to_string(obj),
@@ -586,18 +586,6 @@ async fn handle_execute_code_step(
.await
{
Ok(result) => {
// Broadcast events from code execution to the thread and event channel.
// Without this, ActionExecuted events from CodeAct tool calls are lost
// and never appear in traces.
for event_kind in &result.events {
let event = ThreadEvent::new(thread.id, event_kind.clone());
if let Some(tx) = event_tx {
let _ = tx.send(event.clone());
}
thread.events.push(event);
}
thread.updated_at = chrono::Utc::now();
let action_results: Vec<serde_json::Value> = result
.action_results
.iter()
@@ -698,11 +686,7 @@ async fn handle_execute_action(
}
thread.events.push(event);
thread.updated_at = chrono::Utc::now();
thread.add_message(ThreadMessage::action_result(
call_id,
action_name,
output.to_string(),
));
thread.add_message(ThreadMessage::action_result(call_id, action_name, output.to_string()));
};
// 1. Find lease for this action
@@ -719,7 +703,6 @@ async fn handle_execute_action(
action_name: name.clone(),
call_id: call_id.clone(),
error,
params_summary: None,
},
&call_id,
&name,
@@ -752,7 +735,6 @@ async fn handle_execute_action(
action_name: name.clone(),
call_id: call_id.clone(),
error: reason,
params_summary: None,
},
&call_id,
&name,
@@ -781,7 +763,6 @@ async fn handle_execute_action(
}
// 4. Execute
let ps = summarize_params(&name, &params);
match effects
.execute_action(&name, params, &lease, &exec_ctx)
.await
@@ -795,7 +776,6 @@ async fn handle_execute_action(
action_name: name.clone(),
call_id: call_id.clone(),
duration_ms: r.duration.as_millis() as u64,
params_summary: ps.clone(),
},
&call_id,
&name,
@@ -819,7 +799,6 @@ async fn handle_execute_action(
action_name: name.clone(),
call_id: call_id.clone(),
error: e.to_string(),
params_summary: ps,
},
&call_id,
&name,
@@ -891,7 +870,6 @@ fn handle_emit_event(
action_name,
call_id,
duration_ms: 0,
params_summary: None,
}
}
"action_failed" => {
@@ -903,18 +881,8 @@ fn handle_emit_event(
action_name,
call_id,
error,
params_summary: None,
}
}
"skill_activated" => {
let names_str = extract_string_kwarg(kwargs, "skill_names").unwrap_or_default();
let skill_names: Vec<String> = names_str
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
EventKind::SkillActivated { skill_names }
}
_ => {
debug!(kind = %kind_str, "orchestrator: unknown event kind, skipping");
return ExtFunctionResult::Return(MontyObject::None);
+4 -102
View File
@@ -14,64 +14,6 @@ use crate::traits::store::Store;
use crate::types::capability::ActionDef;
use crate::types::project::ProjectId;
/// Runtime platform metadata injected into system prompts for self-awareness.
///
/// Provides the agent with knowledge about its own identity and environment
/// so it can answer questions about itself, its capabilities, and its
/// configuration without relying on training data.
#[derive(Debug, Clone, Default)]
pub struct PlatformInfo {
/// Software version (from CARGO_PKG_VERSION).
pub version: Option<String>,
/// LLM backend name (e.g. "nearai", "openai", "anthropic").
pub llm_backend: Option<String>,
/// Active model name.
pub model_name: Option<String>,
/// Database backend (e.g. "libsql", "postgres").
pub database_backend: Option<String>,
/// Active channel names (e.g. ["telegram", "cli"]).
pub active_channels: Vec<String>,
/// Owner identifier.
pub owner_id: Option<String>,
/// Project repository URL.
pub repo_url: Option<String>,
}
impl PlatformInfo {
/// Format as a prompt section. Returns empty string if no info is set.
pub fn to_prompt_section(&self) -> String {
let mut lines = Vec::new();
lines.push("You are **IronClaw**, a secure autonomous AI assistant platform.".into());
if let Some(ref v) = self.version {
lines.push(format!("- Version: {v}"));
}
if let Some(ref repo) = self.repo_url {
lines.push(format!("- Repository: {repo}"));
}
if let Some(ref owner) = self.owner_id {
lines.push(format!("- Owner: {owner}"));
}
if let Some(ref backend) = self.llm_backend {
let model = self.model_name.as_deref().unwrap_or("default");
lines.push(format!("- LLM: {backend} ({model})"));
}
if let Some(ref db) = self.database_backend {
lines.push(format!("- Database: {db}"));
}
if !self.active_channels.is_empty() {
lines.push(format!("- Channels: {}", self.active_channels.join(", ")));
}
if lines.len() <= 1 {
// Only the identity line, no runtime details — still include it
return format!("\n\n## Platform\n\n{}\n", lines[0]);
}
format!("\n\n## Platform\n\n{}\n", lines.join("\n"))
}
}
/// The main instruction block (before tool listing).
const CODEACT_PREAMBLE: &str = include_str!("../../prompts/codeact_preamble.md");
@@ -104,15 +46,9 @@ pub async fn build_codeact_system_prompt(
actions: &[ActionDef],
store: Option<&Arc<dyn Store>>,
project_id: ProjectId,
platform: Option<&PlatformInfo>,
) -> String {
let mut prompt = String::from(CODEACT_PREAMBLE);
// Inject platform identity and runtime metadata
if let Some(info) = platform {
prompt.push_str(&info.to_prompt_section());
}
// Append runtime prompt overlay if available
if let Some(store) = store
&& let Some(overlay) = load_prompt_overlay(store, project_id).await
@@ -166,8 +102,7 @@ mod tests {
#[tokio::test]
async fn prompt_without_store_uses_compiled_preamble() {
let prompt =
build_codeact_system_prompt(&[], None, ProjectId(uuid::Uuid::nil()), None).await;
let prompt = build_codeact_system_prompt(&[], None, ProjectId(uuid::Uuid::nil())).await;
assert!(prompt.contains("Python REPL environment"));
assert!(prompt.contains("Strategy"));
assert!(!prompt.contains("Learned Rules"));
@@ -191,8 +126,7 @@ mod tests {
let store = Arc::new(crate::tests::InMemoryStore::with_docs(vec![overlay]));
let prompt =
build_codeact_system_prompt(&[], Some(&(store as Arc<dyn Store>)), project_id, None)
.await;
build_codeact_system_prompt(&[], Some(&(store as Arc<dyn Store>)), project_id).await;
assert!(prompt.contains("Learned Rules"));
assert!(prompt.contains("Never call web_fetch"));
}
@@ -218,8 +152,7 @@ mod tests {
let store = Arc::new(crate::tests::InMemoryStore::with_docs(vec![overlay]));
let prompt =
build_codeact_system_prompt(&[], Some(&(store as Arc<dyn Store>)), project_id, None)
.await;
build_codeact_system_prompt(&[], Some(&(store as Arc<dyn Store>)), project_id).await;
let snowman_count = prompt.chars().filter(|c| *c == '\u{2603}').count();
assert_eq!(snowman_count, MAX_PROMPT_OVERLAY_CHARS);
@@ -244,39 +177,8 @@ mod tests {
let store = Arc::new(crate::tests::InMemoryStore::with_docs(vec![overlay]));
let prompt =
build_codeact_system_prompt(&[], Some(&(store as Arc<dyn Store>)), project_id, None)
.await;
build_codeact_system_prompt(&[], Some(&(store as Arc<dyn Store>)), project_id).await;
assert!(!prompt.contains("Should not appear"));
assert!(!prompt.contains("Learned Rules"));
}
#[tokio::test]
async fn prompt_with_platform_info_injects_identity() {
let info = PlatformInfo {
version: Some("1.2.3".into()),
llm_backend: Some("nearai".into()),
model_name: Some("qwen3-235b".into()),
database_backend: Some("libsql".into()),
active_channels: vec!["telegram".into(), "cli".into()],
owner_id: Some("alice.near".into()),
repo_url: Some("https://github.com/nearai/ironclaw".into()),
};
let prompt =
build_codeact_system_prompt(&[], None, ProjectId(uuid::Uuid::nil()), Some(&info)).await;
assert!(prompt.contains("IronClaw"));
assert!(prompt.contains("1.2.3"));
assert!(prompt.contains("nearai"));
assert!(prompt.contains("qwen3-235b"));
assert!(prompt.contains("libsql"));
assert!(prompt.contains("telegram"));
assert!(prompt.contains("alice.near"));
assert!(prompt.contains("github.com/nearai/ironclaw"));
}
#[tokio::test]
async fn prompt_without_platform_info_has_no_platform_section() {
let prompt =
build_codeact_system_prompt(&[], None, ProjectId(uuid::Uuid::nil()), None).await;
assert!(!prompt.contains("## Platform"));
}
}
@@ -437,18 +437,6 @@ pub async fn execute_code_with_skills(
.await
}
// globals() / locals() — return dict with known names so
// `"tool_name" in globals()` works for capability probing
"globals" | "locals" => {
let entries: Vec<(MontyObject, MontyObject)> = known_actions
.iter()
.map(|name| {
(MontyObject::String(name.clone()), MontyObject::Bool(true))
})
.collect();
ExtFunctionResult::Return(MontyObject::Dict(entries.into()))
}
// Regular tool dispatch
_ => {
let dispatch = dispatch_action(
@@ -530,13 +518,6 @@ pub async fn execute_code_with_skills(
name: name.clone(),
docstring: None,
})
} else if name == "globals" || name == "locals" {
// Python builtins for namespace introspection — resolve as
// callable so code like `"tool" in globals()` works.
NameLookupResult::Value(MontyObject::Function {
name: name.clone(),
docstring: None,
})
} else {
debug!(name = %name, "Monty: unresolved name");
NameLookupResult::Undefined
@@ -990,7 +971,6 @@ async fn dispatch_action(
action_name: action_name.into(),
call_id: call_id.into(),
error: format!("no lease for action '{action_name}'"),
params_summary: None,
});
return DispatchResult::Ok(ExtFunctionResult::NotFound(action_name.into()));
}
@@ -1010,7 +990,6 @@ async fn dispatch_action(
action_name: action_name.into(),
call_id: call_id.into(),
error: reason.clone(),
params_summary: None,
});
return DispatchResult::Ok(ExtFunctionResult::Error(MontyException::new(
ExcType::RuntimeError,
@@ -1035,8 +1014,6 @@ async fn dispatch_action(
)));
}
let ps = crate::types::event::summarize_params(action_name, &params);
match effects
.execute_action(action_name, params, &lease, context)
.await
@@ -1047,7 +1024,6 @@ async fn dispatch_action(
action_name: action_name.into(),
call_id: call_id.into(),
duration_ms: result.duration.as_millis() as u64,
params_summary: ps,
});
let monty_obj = json_to_monty(&result.output);
action_results.push(result);
@@ -1066,7 +1042,6 @@ async fn dispatch_action(
action_name: action_name.into(),
call_id: call_id.into(),
error: e.to_string(),
params_summary: ps,
});
DispatchResult::Ok(ExtFunctionResult::Error(MontyException::new(
ExcType::RuntimeError,
@@ -68,7 +68,6 @@ pub async fn execute_action_calls(
action_name: call.action_name.clone(),
call_id: call.id.clone(),
error: format!("no lease for action '{}'", call.action_name),
params_summary: None,
});
results.push(error_result);
continue;
@@ -98,7 +97,6 @@ pub async fn execute_action_calls(
action_name: call.action_name.clone(),
call_id: call.id.clone(),
error: reason,
params_summary: None,
});
results.push(error_result);
continue;
@@ -140,35 +138,9 @@ pub async fn execute_action_calls(
action_name: call.action_name.clone(),
call_id: call.id.clone(),
duration_ms: action_result.duration.as_millis() as u64,
params_summary: None,
});
results.push(action_result);
}
Err(crate::types::error::EngineError::NeedAuthentication {
credential_name,
action_name,
call_id,
parameters,
}) => {
// Interrupt the batch — thread should pause for authentication.
events.push(EventKind::ActionFailed {
step_id: context.step_id,
action_name: action_name.clone(),
call_id: call_id.clone(),
error: format!("authentication required for credential '{credential_name}'"),
params_summary: None,
});
return Ok(ActionBatchResult {
results,
events,
need_approval: Some(ThreadOutcome::NeedAuthentication {
credential_name,
action_name,
call_id,
parameters,
}),
});
}
Err(e) => {
let error_result = ActionResult {
call_id: call.id.clone(),
@@ -182,7 +154,6 @@ pub async fn execute_action_calls(
action_name: call.action_name.clone(),
call_id: call.id.clone(),
error: e.to_string(),
params_summary: None,
});
results.push(error_result);
}
@@ -277,12 +248,7 @@ mod tests {
#[tokio::test]
async fn call_id_preserved_on_successful_execution() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let thread = Thread::new("test", ThreadType::Foreground, ProjectId::new(), ThreadConfig::default());
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("web_search")],
vec![Ok(ActionResult {
@@ -316,17 +282,9 @@ mod tests {
assert!(!result.results[0].is_error);
// Event should carry the same call_id
let exec_event = result
.events
.iter()
.find(|e| matches!(e, EventKind::ActionExecuted { .. }));
let exec_event = result.events.iter().find(|e| matches!(e, EventKind::ActionExecuted { .. }));
assert!(exec_event.is_some());
if let Some(EventKind::ActionExecuted {
call_id,
action_name,
..
}) = exec_event
{
if let Some(EventKind::ActionExecuted { call_id, action_name, .. }) = exec_event {
assert_eq!(call_id, "call_r2o5mqBgdNUlH8KzskncUGaX");
assert_eq!(action_name, "web_search");
}
@@ -334,12 +292,7 @@ mod tests {
#[tokio::test]
async fn call_id_preserved_on_execution_error() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let thread = Thread::new("test", ThreadType::Foreground, ProjectId::new(), ThreadConfig::default());
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("shell")],
vec![Err(EngineError::Effect {
@@ -366,10 +319,7 @@ mod tests {
assert_eq!(result.results[0].call_id, "call_abc123def");
assert!(result.results[0].is_error);
let fail_event = result
.events
.iter()
.find(|e| matches!(e, EventKind::ActionFailed { .. }));
let fail_event = result.events.iter().find(|e| matches!(e, EventKind::ActionFailed { .. }));
assert!(fail_event.is_some());
if let Some(EventKind::ActionFailed { call_id, .. }) = fail_event {
assert_eq!(call_id, "call_abc123def");
@@ -378,12 +328,7 @@ mod tests {
#[tokio::test]
async fn call_id_preserved_when_no_lease() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let thread = Thread::new("test", ThreadType::Foreground, ProjectId::new(), ThreadConfig::default());
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(vec![], vec![]));
let leases = Arc::new(LeaseManager::new());
let policy = Arc::new(PolicyEngine::new());
@@ -414,12 +359,7 @@ mod tests {
#[tokio::test]
async fn multiple_calls_each_get_correct_call_id() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let thread = Thread::new("test", ThreadType::Foreground, ProjectId::new(), ThreadConfig::default());
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("tool_a"), test_action("tool_b")],
vec![
@@ -467,197 +407,11 @@ mod tests {
assert_eq!(result.results[1].call_id, "id_bbbb");
}
// ── NeedAuthentication tests ─────────────────────────────
#[tokio::test]
async fn need_authentication_interrupts_batch() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("http")],
vec![Err(EngineError::NeedAuthentication {
credential_name: "github_token".into(),
action_name: "http".into(),
call_id: "call_auth_1".into(),
parameters: serde_json::json!({"url": "https://api.github.com/repos"}),
})],
));
let leases = Arc::new(LeaseManager::new());
let policy = Arc::new(PolicyEngine::new());
let ctx = make_exec_context(&thread);
leases.grant(thread.id, "tools", vec![], None, None).await;
let calls = vec![ActionCall {
id: "call_auth_1".into(),
action_name: "http".into(),
parameters: serde_json::json!({"url": "https://api.github.com/repos"}),
}];
let result = execute_action_calls(&calls, &thread, &effects, &leases, &policy, &ctx, &[])
.await
.unwrap();
// Batch should be interrupted with NeedAuthentication outcome
assert!(
result.need_approval.is_some(),
"NeedAuthentication should interrupt the batch"
);
match result.need_approval.unwrap() {
ThreadOutcome::NeedAuthentication {
credential_name,
action_name,
..
} => {
assert_eq!(credential_name, "github_token");
assert_eq!(action_name, "http");
}
other => panic!("expected NeedAuthentication, got {:?}", other),
}
// ActionFailed event should be emitted
assert!(
result
.events
.iter()
.any(|e| matches!(e, EventKind::ActionFailed { .. })),
"should emit ActionFailed event"
);
}
#[tokio::test]
async fn need_authentication_stops_before_subsequent_calls() {
// Two calls: first needs auth, second should never execute
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("http"), test_action("echo")],
vec![
Err(EngineError::NeedAuthentication {
credential_name: "api_key".into(),
action_name: "http".into(),
call_id: "call_1".into(),
parameters: serde_json::json!({}),
}),
// This should never be called
Ok(ActionResult {
call_id: String::new(),
action_name: "echo".into(),
output: serde_json::json!("should not appear"),
is_error: false,
duration: Duration::from_millis(1),
}),
],
));
let leases = Arc::new(LeaseManager::new());
let policy = Arc::new(PolicyEngine::new());
let ctx = make_exec_context(&thread);
leases.grant(thread.id, "tools", vec![], None, None).await;
let calls = vec![
ActionCall {
id: "call_1".into(),
action_name: "http".into(),
parameters: serde_json::json!({}),
},
ActionCall {
id: "call_2".into(),
action_name: "echo".into(),
parameters: serde_json::json!({}),
},
];
let result = execute_action_calls(&calls, &thread, &effects, &leases, &policy, &ctx, &[])
.await
.unwrap();
// Second call should NOT have executed
assert!(
result.results.is_empty(),
"no results should be returned before the interrupted call"
);
assert!(result.need_approval.is_some());
}
/// Regular EngineError::Effect (not NeedAuthentication) should NOT interrupt —
/// it becomes a normal error result and execution continues.
#[tokio::test]
async fn regular_effect_error_does_not_interrupt() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("http"), test_action("echo")],
vec![
Err(EngineError::Effect {
reason: "connection timeout".into(),
}),
Ok(ActionResult {
call_id: String::new(),
action_name: "echo".into(),
output: serde_json::json!("second call ran"),
is_error: false,
duration: Duration::from_millis(1),
}),
],
));
let leases = Arc::new(LeaseManager::new());
let policy = Arc::new(PolicyEngine::new());
let ctx = make_exec_context(&thread);
leases.grant(thread.id, "tools", vec![], None, None).await;
let calls = vec![
ActionCall {
id: "call_1".into(),
action_name: "http".into(),
parameters: serde_json::json!({}),
},
ActionCall {
id: "call_2".into(),
action_name: "echo".into(),
parameters: serde_json::json!({}),
},
];
let result = execute_action_calls(&calls, &thread, &effects, &leases, &policy, &ctx, &[])
.await
.unwrap();
// Both calls should have results (error does not interrupt)
assert_eq!(result.results.len(), 2);
assert!(result.results[0].is_error);
assert!(!result.results[1].is_error);
assert!(
result.need_approval.is_none(),
"no interruption for regular errors"
);
}
// ── call_id preservation (OpenAI/Mistral) ─────────────────
/// Provider-specific: OpenAI rejects empty string call_id. Verify no result
/// ever has an empty call_id when the ActionCall provided one.
#[tokio::test]
async fn openai_empty_call_id_never_produced() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let thread = Thread::new("test", ThreadType::Foreground, ProjectId::new(), ThreadConfig::default());
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("echo")],
vec![Ok(ActionResult {
@@ -694,12 +448,7 @@ mod tests {
/// but engine must never lose it).
#[tokio::test]
async fn mistral_format_call_id_preserved() {
let thread = Thread::new(
"test",
ThreadType::Foreground,
ProjectId::new(),
ThreadConfig::default(),
);
let thread = Thread::new("test", ThreadType::Foreground, ProjectId::new(), ThreadConfig::default());
let effects: Arc<dyn EffectExecutor> = Arc::new(MockEffects::new(
vec![test_action("web_search")],
vec![Ok(ActionResult {
+2 -9
View File
@@ -481,7 +481,6 @@ mod tests {
action_name: "web_search".into(),
call_id: "call_123".into(),
error: "No lease for action 'web_search'".into(),
params_summary: None,
},
));
@@ -547,19 +546,13 @@ mod tests {
thread.add_message(ThreadMessage::assistant("parallel calls"));
thread.add_message(ThreadMessage::action_result("", "tool_a", "result_a"));
thread.add_message(ThreadMessage::action_result("", "tool_b", "result_b"));
thread.add_message(ThreadMessage::action_result(
"call_ok", "tool_c", "result_c",
));
thread.add_message(ThreadMessage::action_result("call_ok", "tool_c", "result_c"));
let issues = analyze_trace(&thread);
let empty_issues: Vec<_> = issues
.iter()
.filter(|i| i.category == "empty_call_id")
.collect();
assert_eq!(
empty_issues.len(),
2,
"should flag exactly the 2 empty call_ids"
);
assert_eq!(empty_issues.len(), 2, "should flag exactly the 2 empty call_ids");
}
}
-1
View File
@@ -55,7 +55,6 @@ pub use capability::registry::CapabilityRegistry;
// ── Re-exports: runtime ───────────────────────────────────────
pub use executor::prompt::PlatformInfo;
pub use runtime::conversation::ConversationManager;
pub use runtime::manager::ThreadManager;
pub use runtime::messaging::ThreadOutcome;
@@ -116,12 +116,12 @@ fn keyword_match_score(doc: &MemoryDoc, keywords: &[String]) -> f64 {
/// Priority weight by doc type. Higher = more useful for context injection.
fn doc_type_weight(doc_type: DocType) -> f64 {
match doc_type {
DocType::Spec => 0.5, // Missing capability info is highest priority
DocType::Skill => 0.45, // Skills with activation metadata and code snippets
DocType::Lesson => 0.4, // Lessons prevent repeating mistakes
DocType::Issue => 0.2, // Known problems
DocType::Summary => 0.1, // Background context
DocType::Note => 0.05, // Scratch notes, lowest priority
DocType::Spec => 0.5, // Missing capability info is highest priority
DocType::Skill => 0.45, // Skills with activation metadata and code snippets
DocType::Lesson => 0.4, // Lessons prevent repeating mistakes
DocType::Issue => 0.2, // Known problems
DocType::Summary => 0.1, // Background context
DocType::Note => 0.05, // Scratch notes, lowest priority
}
}
@@ -249,18 +249,6 @@ impl ConversationManager {
));
// Thread stays active — waiting for approval
}
ThreadOutcome::NeedAuthentication {
credential_name,
action_name: _,
call_id: _,
parameters: _,
} => {
conv.add_entry(ConversationEntry::system_for_thread(
thread_id,
format!("Authentication required for credential: {credential_name}"),
));
// Thread stays active — waiting for OAuth completion
}
}
self.store.save_conversation(conv).await?;
}
@@ -261,9 +261,10 @@ impl ThreadManager {
// Transition Completed → Done
if exec.thread.state == crate::types::thread::ThreadState::Completed
&& let Err(e) = exec
.thread
.transition_to(crate::types::thread::ThreadState::Done, None)
&& let Err(e) = exec.thread.transition_to(
crate::types::thread::ThreadState::Done,
None,
)
{
tracing::warn!(thread_id = %thread_id, "failed to transition to Done: {e}");
}
@@ -38,14 +38,6 @@ pub enum ThreadOutcome {
call_id: String,
parameters: serde_json::Value,
},
/// An action needs a credential that requires user authentication (e.g. OAuth).
/// The thread pauses until the credential is available, then resumes.
NeedAuthentication {
credential_name: String,
action_name: String,
call_id: String,
parameters: serde_json::Value,
},
}
/// A mailbox for sending signals to a running thread.
+223 -39
View File
@@ -387,10 +387,9 @@ impl MissionManager {
.count();
if thread.state == crate::types::thread::ThreadState::Done
&& trace
.issues
.iter()
.all(|i| i.severity != crate::executor::trace::IssueSeverity::Error)
&& trace.issues.iter().all(|i| {
i.severity != crate::executor::trace::IssueSeverity::Error
})
&& thread.step_count >= SKILL_EXTRACTION_MIN_STEPS
&& action_count >= SKILL_EXTRACTION_MIN_ACTIONS
{
@@ -435,28 +434,37 @@ impl MissionManager {
// ── Trigger 3: Conversation insights ────────────
// Use the thread's project_id as a proxy for conversation scope.
let conv_key = thread.project_id.0.to_string();
let count = conv_thread_counts.entry(conv_key.clone()).or_insert(0);
let count = conv_thread_counts
.entry(conv_key.clone())
.or_insert(0);
*count += 1;
if (*count).is_multiple_of(CONVERSATION_INSIGHTS_INTERVAL) {
// Collect recent thread goals for context
let thread_goals: Vec<String> =
match mgr.store.list_threads(thread.project_id).await {
Ok(threads) => threads
.iter()
.rev()
.take(CONVERSATION_INSIGHTS_INTERVAL as usize)
.map(|t| t.goal.clone())
.collect(),
Err(_) => vec![thread.goal.clone()],
};
let thread_goals: Vec<String> = match mgr
.store
.list_threads(thread.project_id)
.await
{
Ok(threads) => threads
.iter()
.rev()
.take(CONVERSATION_INSIGHTS_INTERVAL as usize)
.map(|t| t.goal.clone())
.collect(),
Err(_) => vec![thread.goal.clone()],
};
// Collect sample user messages from recent threads
let sample_messages: Vec<String> = thread
.messages
.iter()
.filter(|m| m.role == crate::types::message::MessageRole::User)
.map(|m| m.content.chars().take(200).collect::<String>())
.filter(|m| {
m.role == crate::types::message::MessageRole::User
})
.map(|m| {
m.content.chars().take(200).collect::<String>()
})
.take(10)
.collect();
@@ -559,7 +567,10 @@ impl MissionManager {
/// Creates (if missing) the self-improvement, skill extraction, and
/// conversation insights missions. This is the preferred entry point —
/// call once at project bootstrap.
pub async fn ensure_learning_missions(&self, project_id: ProjectId) -> Result<(), EngineError> {
pub async fn ensure_learning_missions(
&self,
project_id: ProjectId,
) -> Result<(), EngineError> {
// 1. Error diagnosis (self-improvement) — existing
self.ensure_self_improvement_mission(project_id).await?;
@@ -593,21 +604,6 @@ impl MissionManager {
)
.await?;
// 4. Expected behavior (user feedback loop)
self.ensure_mission_by_metadata(
project_id,
"expected_behavior",
"expected-behavior",
EXPECTED_BEHAVIOR_GOAL,
MissionCadence::OnSystemEvent {
source: "user_feedback".into(),
event_type: "expected_behavior".into(),
},
"Investigate user-reported expectation gaps and apply fixes",
5, // max 5/day
)
.await?;
Ok(())
}
@@ -1017,7 +1013,85 @@ fn extract_json_from_response(response: &str) -> Option<serde_json::Value> {
/// This is the "program.md" — a concrete, step-by-step prompt that tells the
/// agent exactly what to do. Inspired by karpathy/autoresearch: the entire
/// research org is a markdown file with an explicit loop.
const SELF_IMPROVEMENT_GOAL: &str = include_str!("../../prompts/mission_self_improvement.md");
const SELF_IMPROVEMENT_GOAL: &str = "\
You are a self-improvement agent for the IronClaw engine. You receive trigger \
payloads containing execution trace issues from completed threads. Your job is \
to diagnose root causes and apply fixes so the same issue doesn't recur.
## What you have access to
- `state[\"trigger_payload\"]` — JSON with `issues` (list of {severity, category, description, step}), \
`error_messages` (actual error text from failed actions), `goal` (what the thread was trying to do), \
and `source_thread_id`.
- All tools: shell, read_file, write_file, apply_patch, web_search, memory_write, etc.
- The codebase at the current working directory.
- The fix pattern database in prior knowledge (if loaded).
## The experiment loop
For each issue in the trigger payload:
1. **Diagnose**: Read the error messages and issue descriptions. Classify the root cause:
- PROMPT: The LLM made a mistake because the system prompt is missing a rule \
(wrong tool name, bad API usage, ignoring tool results)
- CONFIG: A default value is wrong (truncation length, iteration limit, timeout)
- CODE: There is a bug in the engine or bridge code (crash, type error, missing conversion)
2. **Check the fix pattern database** in prior knowledge. Has this pattern been seen before? \
If yes, apply the known strategy. If no, proceed to step 3.
3. **Apply the fix** based on the level:
Level 1 (PROMPT — low risk, apply directly):
- Read the current prompt overlay: `memory_search(\"prompt:codeact_preamble\")`
- Write an updated overlay with a new rule appended
- Use `memory_write` with title=\"prompt:codeact_preamble\" and tags=[\"prompt_overlay\"]
- The rule should be specific and actionable (e.g. \"Never call web_fetch — use http() instead\")
Level 2 (CONFIG — medium risk):
- Use `read_file` to find the relevant constant or default
- Use `shell` to create a git branch: `git checkout -b self-improve/issue-description`
- Apply the change with `apply_patch` or `write_file`
- Run tests: `cargo test -p ironclaw_engine`
- If tests pass, commit. If not, revert: `git checkout main`
Level 3 (CODE — high risk, just propose):
- Read the relevant source files
- Describe the fix needed but DO NOT apply it directly
- Log it as a recommendation in your FINAL() response
4. **Record what you did** — include in your FINAL() response:
- What issue you analyzed
- What level fix you applied (1/2/3)
- What specific change you made
- Next focus: what to look for next time
## Important rules
- Be specific. \"Never call web_fetch\" is good. \"Be careful with tool names\" is useless.
- One fix per issue. Don't try to fix everything at once.
- For Level 1 fixes, the rule must be one sentence that can be appended to the prompt.
- If the trigger payload has no actionable issues (only Info severity), skip and call FINAL() immediately.
- NEVER modify test files to make a fix pass.
- NEVER modify security-sensitive code (safety layer, policy engine, leak detection).
- If you can't diagnose the root cause after reading the errors, log it and move on.
## Level 1.5: Orchestrator patches (medium risk, auto-rollback)
The execution loop itself is Python code that you can modify. This is the \
orchestrator — it handles tool dispatch, output formatting, state management, \
and context building. If the bug is in the glue between the LLM and tools \
(wrong output format, bad truncation, missing state), you can patch it directly.
To modify the orchestrator:
1. Read current version: `memory_search(\"orchestrator:main\")`
2. Make your change (keep it minimal — one fix at a time)
3. Save the new version: `memory_write` with title=\"orchestrator:main\", \
tags=[\"orchestrator_code\"], metadata={\"version\": N+1, \"parent_version\": N}
4. The next thread will use your updated orchestrator
If your change causes 3 consecutive failures, the system auto-rolls back to \
the previous version. So be conservative — test your logic mentally before saving.";
/// Well-known title for the fix pattern database.
pub const FIX_PATTERN_DB_TITLE: &str = "fix_pattern_database";
@@ -1026,14 +1100,124 @@ pub const FIX_PATTERN_DB_TITLE: &str = "fix_pattern_database";
pub const FIX_PATTERN_DB_TAG: &str = "fix_patterns";
/// The goal for the skill extraction mission.
const SKILL_EXTRACTION_GOAL: &str = include_str!("../../prompts/mission_skill_extraction.md");
const SKILL_EXTRACTION_GOAL: &str = "\
You extract reusable skills from successfully completed multi-step threads.
## Input
`state[\"trigger_payload\"]` contains:
- `source_thread_id` — the thread that completed successfully
- `goal` — what the thread accomplished
- `step_count` — number of execution steps
- `action_count` — number of tool actions executed
- `actions_used` — list of tool names used
- `total_tokens` — tokens consumed
## Output Format
Save as a Skill memory doc via `memory_write(target=\"memory\", content=skill_prompt)` with:
- title: `\"skill:<short-name>\"` (e.g., \"skill:github-issue-triage\")
- doc_type: `\"skill\"`
- metadata JSON:
```json
{
\"name\": \"<short-name>\",
\"version\": 1,
\"description\": \"<one-line description>\",
\"activation\": {
\"keywords\": [\"<keyword1>\", \"<keyword2>\"],
\"patterns\": [\"<optional regex>\"],
\"tags\": [\"<domain-tag>\"],
\"exclude_keywords\": [],
\"max_context_tokens\": <estimated budget, e.g. 1000>
},
\"source\": \"extracted\",
\"trust\": \"trusted\",
\"code_snippets\": [
{
\"name\": \"<function_name>\",
\"code\": \"def <function_name>(...):\\n ...\",
\"description\": \"<what it does>\"
}
],
\"metrics\": {\"usage_count\": 0, \"success_count\": 0, \"failure_count\": 0},
\"content_hash\": \"\"
}
```
## Process
1. Search for the source thread's context: `memory_search(query=goal)`
2. Check for existing skills: `memory_search(query=\"skill:\")`
3. If a similar skill exists, update it (increment version) rather than creating a duplicate
4. Extract:
- Activation keywords from the goal + user messages (be specific, not generic)
- Step-by-step instructions as the prompt content
- Python code snippets for CodeAct (reusable functions using exact tool names)
- Domain tags (e.g., \"github\", \"api\", \"data\")
## Output (FINAL)
Report what you did:
- The skill title and a one-line summary
- Whether it is new or an update to an existing skill
- Next focus: what patterns to watch for
## Rules
- Only extract skills from threads with 3+ distinct tool calls
- Keywords must be specific (not generic words like \"help\", \"do\", \"make\")
- Code snippets must use exact tool function names as they appear in the thread
- If the thread was a trivial query-response, call FINAL(\"No skill needed — simple interaction\") \
and stop immediately
- One skill per FINAL — do not combine unrelated procedures
";
/// The goal for the conversation insights mission.
const CONVERSATION_INSIGHTS_GOAL: &str =
include_str!("../../prompts/mission_conversation_insights.md");
const CONVERSATION_INSIGHTS_GOAL: &str = "\
You extract user preferences, patterns, and domain knowledge from a batch of recent \
conversation threads.
/// The goal for the expected-behavior mission (user feedback loop).
const EXPECTED_BEHAVIOR_GOAL: &str = include_str!("../../prompts/mission_expected_behavior.md");
## Input
`state[\"trigger_payload\"]` contains:
- `project_id` — the project scope
- `completed_thread_count` — total threads completed in this conversation
- `thread_goals` — list of recent thread goals (what the user asked for)
- `sample_user_messages` — sample of actual user messages (truncated to 200 chars)
## Process
1. Analyze the thread goals and user messages for patterns
2. Search existing insights: `memory_search(query=\"user preferences\")` and \
`memory_search(query=\"domain knowledge\")`
3. Extract NEW insights not already recorded in memory
4. Write each insight to memory via `memory_write(target=\"memory\", content=insight_text)` \
with title format \"insight:<category>:<topic>\"
## Categories to look for
- **Preferences**: communication style, format choices, tool preferences
- **Domain**: project names, API patterns, data formats, technology stack
- **Workflow**: recurring task sequences, common follow-up questions
- **Corrections**: things the user corrected or repeated — these signal unmet expectations
## Output (FINAL)
Report:
- Number of new insights extracted (0 is fine)
- Brief list of what was found
- Next focus
## Rules
- Only record actionable, specific insights — not vague observations
- Do not record personal information, only work patterns
- If no meaningful new insights after analysis, call FINAL(\"No new insights — \
conversation patterns already captured\") immediately
- Merge with existing insight docs rather than creating duplicates
- Max 5 insights per run to keep quality high
";
/// Seed content for the fix pattern database.
const SEED_FIX_PATTERNS: &str = "\
@@ -58,14 +58,6 @@ pub enum EngineError {
#[error("skill error: {reason}")]
Skill { reason: String },
#[error("authentication required for credential '{credential_name}'")]
NeedAuthentication {
credential_name: String,
action_name: String,
call_id: String,
parameters: serde_json::Value,
},
}
use crate::types::project::ProjectId;
-76
View File
@@ -8,71 +8,6 @@ use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::types::capability::LeaseId;
/// Generate a short human-readable summary of tool parameters for display.
///
/// For `http`: shows the URL. For `web_search`: shows the query.
/// For other tools: shows the first string argument, truncated.
/// Returns `None` for empty or unrecognizable params.
pub fn summarize_params(action_name: &str, params: &serde_json::Value) -> Option<String> {
let summary = match action_name {
"http" | "web_fetch" => params.get("url").and_then(|v| v.as_str()).map(|u| {
if u.len() > 80 {
format!("{}...", &u[..77])
} else {
u.to_string()
}
}),
"web_search" | "llm_context" => params
.get("query")
.and_then(|v| v.as_str())
.map(|q| truncate(q, 60)),
"memory_search" => params
.get("query")
.and_then(|v| v.as_str())
.map(|q| truncate(q, 60)),
"memory_write" => params
.get("target")
.and_then(|v| v.as_str())
.map(|t| t.to_string()),
"memory_read" => params
.get("path")
.and_then(|v| v.as_str())
.map(|p| p.to_string()),
"shell" => params
.get("command")
.and_then(|v| v.as_str())
.map(|c| truncate(c, 60)),
"message" => params
.get("content")
.and_then(|v| v.as_str())
.map(|c| truncate(c, 40)),
_ => {
// Generic: show first string value
if let Some(obj) = params.as_object() {
obj.values()
.find_map(|v| v.as_str())
.map(|s| truncate(s, 50))
} else {
None
}
}
};
summary.filter(|s| !s.is_empty())
}
fn truncate(s: &str, max: usize) -> String {
if s.len() <= max {
s.to_string()
} else {
// Find a safe UTF-8 boundary
let mut end = max.min(s.len());
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
format!("{}...", &s[..end])
}
}
use crate::types::step::{StepId, TokenUsage};
use crate::types::thread::{ThreadId, ThreadState};
@@ -141,18 +76,12 @@ pub enum EventKind {
action_name: String,
call_id: String,
duration_ms: u64,
/// Short human-readable summary of parameters (e.g., URL for http tool).
#[serde(default, skip_serializing_if = "Option::is_none")]
params_summary: Option<String>,
},
ActionFailed {
step_id: StepId,
action_name: String,
call_id: String,
error: String,
/// Short human-readable summary of parameters.
#[serde(default, skip_serializing_if = "Option::is_none")]
params_summary: Option<String>,
},
// ── Capability leases ───────────────────────────────────
@@ -203,11 +132,6 @@ pub enum EventKind {
error: String,
},
// ── Skill activation ───────────────────────────────────────
SkillActivated {
skill_names: Vec<String>,
},
// ── Orchestrator versioning ───────────────────────────────
OrchestratorRollback {
from_version: u64,
+7 -7
View File
@@ -50,20 +50,20 @@ pub mod registry;
// Re-export core types at crate root for convenience.
pub use types::{
ActivationCriteria, GatingRequirements, LoadedSkill, MAX_PROMPT_FILE_SIZE, OpenClawMeta,
ProviderRefreshStrategy, SkillCredentialLocation, SkillCredentialSpec, SkillManifest,
SkillMetadata, SkillOAuthConfig, SkillSource, SkillTrust,
ActivationCriteria, GatingRequirements, LoadedSkill, OpenClawMeta, ProviderRefreshStrategy,
SkillCredentialLocation, SkillCredentialSpec, SkillManifest, SkillMetadata, SkillOAuthConfig,
SkillSource, SkillTrust, MAX_PROMPT_FILE_SIZE,
};
pub use gating::{GatingResult, check_requirements, check_requirements_sync};
pub use parser::{ParsedSkill, SkillParseError, parse_skill_md};
pub use selector::{MAX_SKILL_CONTEXT_TOKENS, prefilter_skills};
pub use selector::{prefilter_skills, MAX_SKILL_CONTEXT_TOKENS};
pub use validation::{
escape_skill_content, escape_xml_attr, normalize_line_endings, validate_credential_name,
validate_credential_spec, validate_skill_name,
};
pub use gating::{GatingResult, check_requirements, check_requirements_sync};
#[cfg(feature = "catalog")]
pub use catalog::{CatalogEntry, CatalogSearchOutcome, SkillCatalog, shared_catalog};
#[cfg(feature = "registry")]
pub use registry::{SkillRegistry, SkillRegistryError, compute_hash};
#[cfg(feature = "catalog")]
pub use catalog::{CatalogEntry, CatalogSearchOutcome, SkillCatalog, shared_catalog};
+4 -7
View File
@@ -290,18 +290,15 @@ impl LoadedSkill {
patterns
.iter()
.filter_map(|p| {
match regex::RegexBuilder::new(p)
.size_limit(MAX_REGEX_SIZE)
.build()
{
.filter_map(
|p| match regex::RegexBuilder::new(p).size_limit(MAX_REGEX_SIZE).build() {
Ok(re) => Some(re),
Err(e) => {
tracing::warn!("Invalid activation regex pattern '{}': {}", p, e);
None
}
}
})
},
)
.collect()
}
}
-38
View File
@@ -158,42 +158,6 @@ Users reported `"No lease for action 'routine_create'"` when asking the engine t
**Fix**: Registered `mission_create`, `mission_list`, `mission_fire`, `mission_pause`, `mission_resume`, `mission_delete` as a `"missions"` capability in `router.rs`. Descriptions mention "routine" so the LLM maps user intent correctly. Removed all `routine_*` aliases from the effect adapter — `routine_*` names added to `is_v1_only_tool()` blocklist with clear error directing to `mission_*`.
## Session 9: Trace Pipeline Fix, Monty Builtins, Self-Awareness (2026-03-28)
Three fixes driven by analyzing a live engine trace (`engine_trace_20260328T030519.json`) from the hourly Iran-region monitor mission.
### Event Pipeline Loss in CodeAct
**The bug**: The `no_tools_used` trace issue fired as a false positive — the mission thread called `web_search` 5 times, `llm_context` once, and `llm_query` once, yet the trace had zero `ActionExecuted` events.
**Root cause**: `handle_execute_code_step()` in `orchestrator.rs` received `CodeExecutionResult::events` (populated by `dispatch_action()` in `scripting.rs`) but never transferred them to `thread.events` or broadcast them via `event_tx`. The function took `&Thread` (immutable) and had no access to the event broadcast channel. Compare with `handle_execute_action()` which correctly calls `emit_and_record()` for each action.
**Fix**: Changed `handle_execute_code_step()` to take `&mut Thread` + `event_tx`, iterate over `result.events`, push each to `thread.events` and broadcast via `event_tx` — same pattern as `handle_execute_action()`. The `no_tools_used` detector in `trace.rs` now works correctly for CodeAct because `ActionExecuted` events are present.
### globals() NameError in Monty
**The bug**: LLM-generated code used `"mission_create" in globals()` to probe available capabilities before calling them. Monty doesn't implement `globals()` as a builtin, so NameLookup returned `Undefined` → NameError → code execution failure.
**Fix**: Added `globals`/`locals` to the NameLookup handler as callable function stubs, and a FunctionCall handler that returns a `Dict` of all known action names (from capability leases) as keys. Code like `"tool_name" in globals()` now works for capability probing.
### Platform Self-Awareness
**The problem**: The agent had no knowledge of its own identity. It didn't know it was IronClaw, its GitHub repo, its version, active channels, LLM backend, or database. The system prompt just said "You are IronClaw Agent, a secure autonomous assistant" with no specifics.
**The insight**: Identity infrastructure was 85% built — `IDENTITY.md`, `SOUL.md`, `USER.md`, `AGENTS.md` injection worked for *user* identity. But nothing existed for *platform* identity. This isn't workspace-level (it changes with runtime config), so a seed file was wrong — it needed to be injected dynamically.
**Implementation** (8 files):
1. **`PlatformInfo` struct** (`executor/prompt.rs`) — version, llm_backend, model_name, database_backend, active_channels, owner_id, repo_url. `to_prompt_section()` renders a `## Platform` block.
2. **CodeAct path**`build_codeact_system_prompt()` accepts optional `PlatformInfo`, injects before tool listing.
3. **Tier 0 path**`Reasoning` struct gets `with_platform_info()` builder, `build_runtime_section()` prepends the platform block.
4. **Runtime wiring**`Agent::platform_info()` constructs from `AgentDeps` (version from `CARGO_PKG_VERSION`, backend/model/owner from deps, channels from `ChannelManager`).
**Test coverage**: 2 new tests (platform info injection + absence). 195 engine tests pass, zero clippy warnings.
## Architecture Evolution
```
@@ -207,8 +171,6 @@ Session 7: Integration scaling: Capabilities as knowledge → http action
(not Pica-style per-action tools — tool list bloat kills LLM accuracy)
Session 8: Skills-based OAuth (credential specs in YAML frontmatter)
+ HTTP tool zero-leak hardening + mission capability leases
Session 9: CodeAct event pipeline fix (ActionExecuted events were lost)
+ Monty globals() builtin + platform self-awareness injection
```
## Key Commits
+61
View File
@@ -0,0 +1,61 @@
---
name: 1-password
version: "1.0.0"
description: 1Password API — 1Password is a secure password manager that consolidates credentials
activation:
keywords:
- "1-password"
- "1password"
- "security"
patterns:
- "(?i)1.?password"
tags:
- "security"
- "identity"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [1PASSWORD_CONNECT_SERVER_URL, 1PASSWORD_CONNECT_ACCESS_TOKEN]
---
# 1Password API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`{1PASSWORD_CONNECT_SERVER_URL}/v1`
## Actions
**List vaults:**
```
http(method="GET", url="{1PASSWORD_CONNECT_SERVER_URL}/v1/vaults")
```
**List items in vault:**
```
http(method="GET", url="{1PASSWORD_CONNECT_SERVER_URL}/v1/vaults/{vault_id}/items")
```
**Get item details:**
```
http(method="GET", url="{1PASSWORD_CONNECT_SERVER_URL}/v1/vaults/{vault_id}/items/{item_id}")
```
**Create item:**
```
http(method="POST", url="{1PASSWORD_CONNECT_SERVER_URL}/v1/vaults/{vault_id}/items", body={"vault": {"id": "<vault_id>"},"category": "LOGIN","title": "My Login","fields": [{"purpose": "USERNAME","value": "user@example.com"},{"purpose": "PASSWORD","value": "secret"}]})
```
**Delete item:**
```
http(method="DELETE", url="{1PASSWORD_CONNECT_SERVER_URL}/v1/vaults/{vault_id}/items/{item_id}")
```
## Notes
- Item categories: `LOGIN`, `PASSWORD`, `SECURE_NOTE`, `CREDIT_CARD`, `IDENTITY`, `DOCUMENT`.
- Fields have `purpose`: `USERNAME`, `PASSWORD`, `NOTES`.
- The Connect server must be running and accessible at the configured URL.
+38
View File
@@ -0,0 +1,38 @@
---
name: 7-shifts
version: "1.0.0"
description: 7shifts API — 7Shifts is a cloudbased workforce management platform tailored for restaurants
activation:
keywords:
- "7-shifts"
- "7shifts"
- "hospitality"
patterns:
- "(?i)7.?shifts"
tags:
- "hospitality"
- "scheduling"
max_context_tokens: 1200
---
# 7shifts API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
> 7Shifts is a cloudbased workforce management platform tailored for restaurants, combining intuitive draganddrop scheduling, mobile time tracking, automated payroll and tip management, labor complia
## Authentication
This integration uses **OAuth 2.0**. The token is managed automatically — no manual auth setup required in API calls.
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: ably-control
version: "1.0.0"
description: Ably Control API — Ably Control API is a RESTful interface that enables developers and DevOps teams
activation:
keywords:
- "ably-control"
- "ably control"
- "tools"
patterns:
- "(?i)ably.?control"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABLY_CONTROL_ACCESS_TOKEN]
---
# Ably Control API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
> Ably Control API is a RESTful interface that enables developers and DevOps teams to programmatically provision, configure, and manage real-time infrastructure—such as apps, API keys, namespaces, queue
## Authentication
This integration uses **Bearer Token** authentication. The token is injected automatically into the `Authorization` header.
## Required Credentials
- `ABLY_CONTROL_ACCESS_TOKEN` — Access Token
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: ably
version: "1.0.0"
description: Ably API — Ably Pub/Sub is a global serverless real-time messaging platform that delivers s
activation:
keywords:
- "ably"
- "tools"
patterns:
- "(?i)ably"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABLY_ENCODED_API_KEY]
---
# Ably API
Use the `http` tool. API key is automatically injected via `Authorization` header — **never construct auth headers manually**.
> Ably Pub/Sub is a global serverless real-time messaging platform that delivers sub60ms latency pub/sub capabilities—including message history, presence detection, exactlyonce delivery, and guarante
## Authentication
This integration uses **API Key** authentication via the `Authorization` header.
Format: `Authorization: Basic ...`
## Required Credentials
- `ABLY_ENCODED_API_KEY` — API Key (RFC 4648 Base64 Encoded)
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+47
View File
@@ -0,0 +1,47 @@
---
name: abstract-avatars
version: "1.0.0"
description: Abstract Avatars API — An API that generates customizable user avatars based on names or unique identif
activation:
keywords:
- "abstract-avatars"
- "abstract avatars"
- "avatar generation"
patterns:
- "(?i)abstract.?avatars"
tags:
- "avatar"
- "images"
- "avatar-generation"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_AVATARS_API_KEY]
---
# Abstract Avatars API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that generates customizable user avatars based on names or unique identifiers, enabling applications to automatically create consistent, visually distinct profile images without requiring users
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_AVATARS_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
@@ -0,0 +1,46 @@
---
name: abstract-company-enrichment
version: "1.0.0"
description: Abstract Company Enrichment API — An API that enriches company records with firmographic data such as industry
activation:
keywords:
- "abstract-company-enrichment"
- "abstract company enrichment"
- "data enrichment"
patterns:
- "(?i)abstract.?company.?enrichment"
tags:
- "data-enrichment"
- "enrichment"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_COMPANY_ENRICHMENT_API_KEY]
---
# Abstract Company Enrichment API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that enriches company records with firmographic data such as industry, size, location, and domain details, enabling businesses to enhance lead profiles, improve segmentation, and power more acc
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_COMPANY_ENRICHMENT_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: abstract-email-reputation
version: "1.0.0"
description: Abstract Email Reputation API — An API that evaluates the reputation of email addresses by analyzing risk factor
activation:
keywords:
- "abstract-email-reputation"
- "abstract email reputation"
- "email verification"
patterns:
- "(?i)abstract.?email.?reputation"
tags:
- "tools"
- "email-verification"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_EMAIL_REPUTATION_API_KEY]
---
# Abstract Email Reputation API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that evaluates the reputation of email addresses by analyzing risk factors to help applications improve deliverability, reduce fraud, and enhance email validation accuracy.
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_EMAIL_REPUTATION_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: abstract-iban-validation
version: "1.0.0"
description: Abstract IBAN Validation API — An API that validates IBAN numbers by checking format, bank details
activation:
keywords:
- "abstract-iban-validation"
- "abstract iban validation"
- "iban validation"
patterns:
- "(?i)abstract.?iban.?validation"
tags:
- "tools"
- "iban-validation"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_IBAN_VALIDATION_API_KEY]
---
# Abstract IBAN Validation API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that validates IBAN numbers by checking format, bank details, and country-specific rules to help businesses prevent payment errors, reduce fraud risk, and streamline international transactions.
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_IBAN_VALIDATION_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: abstract-ip-intelligence
version: "1.0.0"
description: Abstract IP Intelligence API — An API that provides IP address data including geolocation, ISP
activation:
keywords:
- "abstract-ip-intelligence"
- "abstract ip intelligence"
- "ip intelligence"
patterns:
- "(?i)abstract.?ip.?intelligence"
tags:
- "tools"
- "ip-intelligence"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_IP_INTELLIGENCE_API_KEY]
---
# Abstract IP Intelligence API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that provides IP address data including geolocation, ISP, proxy and VPN detection, and risk signals to help applications enhance security, prevent fraud, and deliver location-aware experiences.
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_IP_INTELLIGENCE_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
@@ -0,0 +1,46 @@
---
name: abstract-phone-intelligence
version: "1.0.0"
description: Abstract Phone Intelligence API — An API that validates and enriches phone numbers with carrier, location
activation:
keywords:
- "abstract-phone-intelligence"
- "abstract phone intelligence"
- "phone validation"
patterns:
- "(?i)abstract.?phone.?intelligence"
tags:
- "tools"
- "phone-validation"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_PHONE_INTELLIGENCE_API_KEY]
---
# Abstract Phone Intelligence API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that validates and enriches phone numbers with carrier, location, and line type data, enabling applications to verify user input, detect fraud risk, and improve communication accuracy globally.
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_PHONE_INTELLIGENCE_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: abstract-public-holidays
version: "1.0.0"
description: Abstract Public Holidays API — An API that delivers official public holiday data by country and year
activation:
keywords:
- "abstract-public-holidays"
- "abstract public holidays"
- "public holidays"
patterns:
- "(?i)abstract.?public.?holidays"
tags:
- "tools"
- "public-holidays"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_PUBLIC_HOLIDAYS_API_KEY]
---
# Abstract Public Holidays API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that delivers official public holiday data by country and year, enabling applications to access national and regional holiday calendars for scheduling, localization, and compliance use cases.
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_PUBLIC_HOLIDAYS_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: abstract-vat-validation
version: "1.0.0"
description: Abstract Vat Validation API — An API that validates VAT numbers against official registries
activation:
keywords:
- "abstract-vat-validation"
- "abstract vat validation"
- "vat validation"
patterns:
- "(?i)abstract.?vat.?validation"
tags:
- "tools"
- "vat-validation"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABSTRACT_VAT_VALIDATION_API_KEY]
---
# Abstract Vat Validation API
Use the `http` tool. API key is automatically injected as `api_key` query parameter.
> An API that validates VAT numbers against official registries, confirms company details, and helps businesses ensure tax compliance, reduce fraud risk, and automate cross-border invoicing workflows.
## Authentication
This integration uses **query parameter** authentication via `api_key`.
## Required Credentials
- `ABSTRACT_VAT_VALIDATION_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: abyssale
version: "1.0.0"
description: Abyssale API — A cloud-based creative automation solution that helps teams design, generate
activation:
keywords:
- "abyssale"
- "creative automation platform"
patterns:
- "(?i)abyssale"
tags:
- "tools"
- "creative-automation-platform"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ABYSSALE_API_KEY]
---
# Abyssale API
Use the `http` tool. API key is automatically injected via `x-api-key` header — **never construct auth headers manually**.
> A cloud-based creative automation solution that helps teams design, generate, and scale thousands of on-brand visual assets in minutes from a single template, accelerates production through APIs and i
## Authentication
This integration uses **API Key** authentication via the `x-api-key` header.
Format: `x-api-key: ...`
## Required Credentials
- `ABYSSALE_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+81
View File
@@ -0,0 +1,81 @@
---
name: activecampaign
version: "1.0.0"
description: ActiveCampaign API — contacts, deals, automations, campaigns, lists
activation:
keywords:
- "activecampaign"
- "active campaign"
- "marketing automation"
exclude_keywords:
- "hubspot"
- "mailchimp"
patterns:
- "(?i)active.?campaign.*(contact|deal|automation|campaign)"
tags:
- "marketing"
- "crm"
- "automation"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ACTIVECAMPAIGN_URL, ACTIVECAMPAIGN_API_KEY]
---
# ActiveCampaign API v3
Use the `http` tool. Include `Api-Token` header.
## Base URL
`https://{ACTIVECAMPAIGN_URL}.api-us1.com/api/3`
## Actions
**List contacts:**
```
http(method="GET", url="https://{account}.api-us1.com/api/3/contacts?limit=20", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}])
```
**Search contacts:**
```
http(method="GET", url="https://{account}.api-us1.com/api/3/contacts?search=john@example.com", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}])
```
**Create contact:**
```
http(method="POST", url="https://{account}.api-us1.com/api/3/contacts", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}], body={"contact": {"email": "john@example.com", "firstName": "John", "lastName": "Doe", "phone": "+1234567890"}})
```
**Update contact:**
```
http(method="PUT", url="https://{account}.api-us1.com/api/3/contacts/<contact_id>", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}], body={"contact": {"firstName": "Updated"}})
```
**List deals:**
```
http(method="GET", url="https://{account}.api-us1.com/api/3/deals?limit=20", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}])
```
**Create deal:**
```
http(method="POST", url="https://{account}.api-us1.com/api/3/deals", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}], body={"deal": {"title": "New Deal", "value": 5000, "currency": "usd", "contact": "<contact_id>", "group": "<pipeline_id>", "stage": "<stage_id>"}})
```
**Add contact to automation:**
```
http(method="POST", url="https://{account}.api-us1.com/api/3/contactAutomations", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}], body={"contactAutomation": {"contact": "<contact_id>", "automation": "<automation_id>"}})
```
**List automations:**
```
http(method="GET", url="https://{account}.api-us1.com/api/3/automations?limit=20", headers=[{"name": "Api-Token", "value": "{ACTIVECAMPAIGN_API_KEY}"}])
```
## Notes
- All request/response bodies wrap data in the resource name: `{"contact": {...}}`, `{"contacts": [...]}`.
- Deal values are in cents (integer).
- Pagination: `limit` + `offset`. Check `meta.total` for count.
- Tags can be added via `/api/3/contactTags` with `{"contactTag": {"contact": "id", "tag": "id"}}`.
+46
View File
@@ -0,0 +1,46 @@
---
name: affinity-co
version: "1.0.0"
description: Affinity.co API — Affinity is a relationship intelligence CRM that automatically captures and anal
activation:
keywords:
- "affinity-co"
- "affinity.co"
- "crm"
patterns:
- "(?i)affinity.?co"
tags:
- "crm"
- "sales"
- "contacts"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [AFFINITY_CO_API_KEY]
---
# Affinity.co API
Use the `http` tool. Credentials are automatically injected.
> Affinity is a relationship intelligence CRM that automatically captures and analyzes your team's communication data to surface valuable connections, streamline deal management, and help you close more
## Authentication
## Required Credentials
- `AFFINITY_CO_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: agent-mail
version: "1.0.0"
description: AgentMail API — Agent Mail enables developers to give AI agents unique
activation:
keywords:
- "agent-mail"
- "agentmail"
- "ai"
patterns:
- "(?i)agent.?mail"
tags:
- "ai"
- "machine-learning"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [AGENT_MAIL_API_KEY]
---
# AgentMail API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
> Agent Mail enables developers to give AI agents unique, programmable email inboxes that can send, receive, and act on emails at scale—featuring API-first integration, custom domains, and built-in deli
## Authentication
This integration uses **Bearer Token** authentication. The token is injected automatically into the `Authorization` header.
## Required Credentials
- `AGENT_MAIL_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+47
View File
@@ -0,0 +1,47 @@
---
name: agent-ql
version: "1.0.0"
description: AgentQL API — AgentQL is a natural language interface that allows users to query their data us
activation:
keywords:
- "agent-ql"
- "agentql"
- "ai"
patterns:
- "(?i)agent.?ql"
tags:
- "ai"
- "machine-learning"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [AGENTQL_API_KEY]
---
# AgentQL API
Use the `http` tool. API key is automatically injected via `X-API-Key` header — **never construct auth headers manually**.
> AgentQL is a natural language interface that allows users to query their data using plain English, enabling seamless interaction with databases and APIs without writing traditional code.
## Authentication
This integration uses **API Key** authentication via the `X-API-Key` header.
Format: `X-API-Key: ...`
## Required Credentials
- `AGENTQL_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+38
View File
@@ -0,0 +1,38 @@
---
name: agiled
version: "1.0.0"
description: Agiled API — Agiled is an all-in-one business management platform that helps freelancers and
activation:
keywords:
- "agiled"
- "crm"
patterns:
- "(?i)agiled"
tags:
- "crm"
- "sales"
- "contacts"
max_context_tokens: 1200
---
# Agiled API
Use the `http` tool. API key is automatically injected as `api_token` query parameter.
> Agiled is an all-in-one business management platform that helps freelancers and small businesses manage CRM, projects, finances, contracts, invoicing and client portals within a unified workspace.
## Authentication
This integration uses **query parameter** authentication via `api_token`.
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+56
View File
@@ -0,0 +1,56 @@
---
name: ahrefs
version: "1.0.0"
description: Ahrefs API — Ahrefs is an all-in-one SEO toolset that helps businesses and marketers improve
activation:
keywords:
- "ahrefs"
- "tools"
patterns:
- "(?i)ahrefs"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [AHREFS_API_KEY]
---
# Ahrefs API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://api.ahrefs.com/v3`
## Actions
**Get domain rating:**
```
http(method="GET", url="https://api.ahrefs.com/v3/site-explorer/domain-rating?target=example.com&date=2026-03-01")
```
**Get backlinks:**
```
http(method="GET", url="https://api.ahrefs.com/v3/site-explorer/all-backlinks?target=example.com&limit=10&mode=subdomains")
```
**Get organic keywords:**
```
http(method="GET", url="https://api.ahrefs.com/v3/site-explorer/organic-keywords?target=example.com&limit=10&country=us")
```
**Get referring domains:**
```
http(method="GET", url="https://api.ahrefs.com/v3/site-explorer/refdomains?target=example.com&limit=10&mode=subdomains")
```
## Notes
- `target` can be a domain, subdomain, or URL.
- `mode`: `exact`, `prefix`, `domain`, `subdomains`.
- Dates in `YYYY-MM-DD` format.
- Results are paginated with `offset` and `limit`.
+85
View File
@@ -0,0 +1,85 @@
---
name: airtable
version: "1.0.0"
description: Airtable API — bases, tables, records, fields, views
activation:
keywords:
- "airtable"
- "airtable base"
- "airtable record"
exclude_keywords:
- "google sheets"
- "notion"
patterns:
- "(?i)airtable.*(base|table|record|field)"
- "(?i)(create|list|update).*airtable"
tags:
- "database"
- "spreadsheet"
- "productivity"
max_context_tokens: 1500
metadata:
openclaw:
requires:
env: [AIRTABLE_ACCESS_TOKEN]
---
# Airtable Web API
Use the `http` tool. Credentials are automatically injected for `api.airtable.com`.
## Base URL
`https://api.airtable.com/v0`
## Actions
**List bases:**
```
http(method="GET", url="https://api.airtable.com/v0/meta/bases")
```
**List tables in base:**
```
http(method="GET", url="https://api.airtable.com/v0/meta/bases/<base_id>/tables")
```
**List records:**
```
http(method="GET", url="https://api.airtable.com/v0/<base_id>/<table_name>?maxRecords=20&view=Grid%20view")
```
**List with filter:**
```
http(method="GET", url="https://api.airtable.com/v0/<base_id>/<table_name>?filterByFormula={Status}='Active'&sort[0][field]=Name&sort[0][direction]=asc&maxRecords=20")
```
**Get record:**
```
http(method="GET", url="https://api.airtable.com/v0/<base_id>/<table_name>/<record_id>")
```
**Create records:**
```
http(method="POST", url="https://api.airtable.com/v0/<base_id>/<table_name>", body={"records": [{"fields": {"Name": "Alice", "Email": "alice@example.com", "Status": "Active"}}]})
```
**Update records:**
```
http(method="PATCH", url="https://api.airtable.com/v0/<base_id>/<table_name>", body={"records": [{"id": "<record_id>", "fields": {"Status": "Done"}}]})
```
**Delete records:**
```
http(method="DELETE", url="https://api.airtable.com/v0/<base_id>/<table_name>?records[]=<record_id1>&records[]=<record_id2>")
```
## Notes
- Base IDs start with `app`, table IDs with `tbl`, record IDs with `rec`.
- Table name in URL must be URL-encoded if it contains spaces.
- `filterByFormula` uses Airtable formula syntax: `{Field Name}='value'`, `AND(...)`, `OR(...)`.
- Create/update accept up to 10 records per request.
- Pagination: use `offset` from response. When absent, no more records.
- Rate limit: 5 requests/second per base.
- Field names are case-sensitive and must match exactly.
+47
View File
@@ -0,0 +1,47 @@
---
name: alt-text-ai
version: "1.0.0"
description: AltText AI API — An AI-driven service that automatically analyzes images and generates descriptiv
activation:
keywords:
- "alt-text-ai"
- "alttext ai"
- "generation tool"
patterns:
- "(?i)alt.?text.?ai"
tags:
- "tools"
- "generation-tool"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ALT_TEXT_AI_API_KEY]
---
# AltText AI API
Use the `http` tool. API key is automatically injected via `X-API-Key` header — **never construct auth headers manually**.
> An AI-driven service that automatically analyzes images and generates descriptive, SEO-friendly alt text to improve website accessibility, enhance search visibility across languages, and streamline im
## Authentication
This integration uses **API Key** authentication via the `X-API-Key` header.
Format: `X-API-Key: ...`
## Required Credentials
- `ALT_TEXT_AI_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+39
View File
@@ -0,0 +1,39 @@
---
name: amazon-ads
version: "1.0.0"
description: Amazon Ads API — Amazon Ads is an advertising platform that enables brands to promote their produ
activation:
keywords:
- "amazon-ads"
- "amazon ads"
- "marketing"
patterns:
- "(?i)amazon.?ads"
tags:
- "marketing"
- "email"
- "campaigns"
max_context_tokens: 1200
---
# Amazon Ads API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
> Amazon Ads is an advertising platform that enables brands to promote their products across Amazons ecosystem, helping them reach shoppers through targeted ads, sponsored listings, and display campaig
## Authentication
This integration uses **OAuth 2.0**. The token is managed automatically — no manual auth setup required in API calls.
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+47
View File
@@ -0,0 +1,47 @@
---
name: anchor-browser
version: "1.0.0"
description: Anchor Browser API — Anchor Browser is a cloud-hosted automation platform that lets AI agents interac
activation:
keywords:
- "anchor-browser"
- "anchor browser"
- "web automation"
patterns:
- "(?i)anchor.?browser"
tags:
- "tools"
- "web-automation"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ANCHOR_BROWSER_API_KEY]
---
# Anchor Browser API
Use the `http` tool. API key is automatically injected via `anchor-api-key` header — **never construct auth headers manually**.
> Anchor Browser is a cloud-hosted automation platform that lets AI agents interact with web pages like a human—navigating sites, clicking, typing, submitting forms and extracting data—so teams can auto
## Authentication
This integration uses **API Key** authentication via the `anchor-api-key` header.
Format: `anchor-api-key: ...`
## Required Credentials
- `ANCHOR_BROWSER_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+47
View File
@@ -0,0 +1,47 @@
---
name: anthropic-admin
version: "1.0.0"
description: Anthropic Admin API — Anthropic Admin provides administrative tools for managing access, API keys
activation:
keywords:
- "anthropic-admin"
- "anthropic admin"
- "ai"
patterns:
- "(?i)anthropic.?admin"
tags:
- "ai"
- "machine-learning"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ANTHROPIC_ADMIN_API_KEY]
---
# Anthropic Admin API
Use the `http` tool. API key is automatically injected via `X-Api-Key` header — **never construct auth headers manually**.
> Anthropic Admin provides administrative tools for managing access, API keys, usage, billing, and organizational settings for applications built with Claude AI models, allowing teams to control permiss
## Authentication
This integration uses **API Key** authentication via the `X-Api-Key` header.
Format: `X-Api-Key: ...`
## Required Credentials
- `ANTHROPIC_ADMIN_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+53
View File
@@ -0,0 +1,53 @@
---
name: anthropic
version: "1.0.0"
description: Anthropic API — Anthropic is an AI safety and research company focused on building reliable
activation:
keywords:
- "anthropic"
- "ai"
patterns:
- "(?i)anthropic"
tags:
- "ai"
- "machine-learning"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [ANTHROPIC_API_KEY]
---
# Anthropic API
Use the `http` tool. API key is automatically injected via `x-api-key` header — **never construct auth headers manually**.
## Base URL
`https://api.anthropic.com/v1`
**Required headers**: `x-api-key: {ANTHROPIC_API_KEY}`, `anthropic-version: 2023-06-01`
## Actions
**Create message:**
```
http(method="POST", url="https://api.anthropic.com/v1/messages", headers=[{"name": "x-api-key","value": "{ANTHROPIC_API_KEY}"},{"name": "anthropic-version","value": "2023-06-01"}], body={"model": "claude-sonnet-4-20250514","max_tokens": 1024,"messages": [{"role": "user","content": "Hello"}]})
```
**Create message with system prompt:**
```
http(method="POST", url="https://api.anthropic.com/v1/messages", headers=[{"name": "x-api-key","value": "{ANTHROPIC_API_KEY}"},{"name": "anthropic-version","value": "2023-06-01"}], body={"model": "claude-sonnet-4-20250514","max_tokens": 1024,"system": "You are a helpful assistant.","messages": [{"role": "user","content": "Explain quantum computing"}]})
```
**List models:**
```
http(method="GET", url="https://api.anthropic.com/v1/models")
```
## Notes
- Always include `anthropic-version: 2023-06-01` header.
- Models: `claude-sonnet-4-20250514`, `claude-haiku-4-5-20251001`, `claude-opus-4-20250514`.
- Max tokens is required for all message requests.
- Streaming: set `stream: true` in body.
+55
View File
@@ -0,0 +1,55 @@
---
name: apify
version: "1.0.0"
description: Apify API — Apify is a fullstack web scraping and browser automation platform where develop
activation:
keywords:
- "apify"
- "tools"
patterns:
- "(?i)apify"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [APIFY_API_KEY]
---
# Apify API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://api.apify.com/v2`
## Actions
**List actors:**
```
http(method="GET", url="https://api.apify.com/v2/acts?limit=10")
```
**Run actor:**
```
http(method="POST", url="https://api.apify.com/v2/acts/{actor_id}/runs", body={"memory": 256,"timeout": 60})
```
**Get run details:**
```
http(method="GET", url="https://api.apify.com/v2/acts/{actor_id}/runs/{run_id}")
```
**Get dataset items:**
```
http(method="GET", url="https://api.apify.com/v2/datasets/{dataset_id}/items?limit=100")
```
## Notes
- Actor IDs look like `username~actor-name` or a hash.
- Run status: `READY`, `RUNNING`, `SUCCEEDED`, `FAILED`, `TIMED-OUT`, `ABORTED`.
- Default dataset is created per run; check `defaultDatasetId` in run details.
+52
View File
@@ -0,0 +1,52 @@
---
name: apollo
version: "1.0.0"
description: Apollo API — Apollo is a sales intelligence and engagement platform that helps teams find and
activation:
keywords:
- "apollo"
- "crm"
patterns:
- "(?i)apollo"
tags:
- "crm"
- "sales"
- "contacts"
max_context_tokens: 1200
---
# Apollo API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://api.apollo.io/api/v1`
## Actions
**Search people:**
```
http(method="POST", url="https://api.apollo.io/api/v1/mixed_people/search", body={"person_titles": ["CEO"],"person_locations": ["San Francisco"],"page": 1,"per_page": 10})
```
**Search organizations:**
```
http(method="POST", url="https://api.apollo.io/api/v1/mixed_companies/search", body={"organization_locations": ["United States"],"page": 1,"per_page": 10})
```
**Get person:**
```
http(method="GET", url="https://api.apollo.io/api/v1/people/{person_id}")
```
**Enrich person:**
```
http(method="POST", url="https://api.apollo.io/api/v1/people/match", body={"email": "john@example.com"})
```
## Notes
- API key goes in the request body as `api_key` or as a header.
- Search supports filters: `person_titles`, `person_locations`, `organization_domains`.
- Rate limit: 50 requests per minute on free tier.
+87
View File
@@ -0,0 +1,87 @@
---
name: asana
version: "1.0.0"
description: Asana REST API — tasks, projects, sections, comments, search
activation:
keywords:
- "asana"
- "asana task"
- "asana project"
exclude_keywords:
- "jira"
- "trello"
patterns:
- "(?i)asana.*(task|project|section)"
- "(?i)(create|list|update|complete).*asana"
tags:
- "project-management"
- "task-management"
max_context_tokens: 1500
metadata:
openclaw:
requires:
env: [ASANA_ACCESS_TOKEN]
---
# Asana REST API
Use the `http` tool. Credentials are automatically injected for `app.asana.com`.
## Base URL
`https://app.asana.com/api/1.0`
## Actions
**List my tasks:**
```
http(method="GET", url="https://app.asana.com/api/1.0/user_task_lists/me/tasks?workspace=<workspace_gid>&opt_fields=name,completed,due_on,assignee_section.name&limit=20")
```
**Get task:**
```
http(method="GET", url="https://app.asana.com/api/1.0/tasks/<task_gid>?opt_fields=name,notes,completed,due_on,assignee.name,projects.name,tags.name")
```
**Create task:**
```
http(method="POST", url="https://app.asana.com/api/1.0/tasks", body={"data": {"name": "Task title", "notes": "Description", "projects": ["<project_gid>"], "due_on": "2026-04-01", "assignee": "me"}})
```
**Update task:**
```
http(method="PUT", url="https://app.asana.com/api/1.0/tasks/<task_gid>", body={"data": {"completed": true}})
```
**List project tasks:**
```
http(method="GET", url="https://app.asana.com/api/1.0/projects/<project_gid>/tasks?opt_fields=name,completed,due_on,assignee.name&limit=50")
```
**Add comment:**
```
http(method="POST", url="https://app.asana.com/api/1.0/tasks/<task_gid>/stories", body={"data": {"text": "Comment text"}})
```
**Search tasks:**
```
http(method="GET", url="https://app.asana.com/api/1.0/workspaces/<workspace_gid>/tasks/search?text=search+term&opt_fields=name,completed&limit=20")
```
**List projects:**
```
http(method="GET", url="https://app.asana.com/api/1.0/projects?workspace=<workspace_gid>&opt_fields=name,current_status&limit=50")
```
**List workspaces:**
```
http(method="GET", url="https://app.asana.com/api/1.0/workspaces")
```
## Notes
- All response data is wrapped in `{"data": ...}`. Errors: `{"errors": [{"message": "..."}]}`.
- Use `opt_fields` to request specific fields (comma-separated). Without it, you get minimal data.
- GIDs are numeric strings like `"1234567890123456"`.
- Dates are ISO format `YYYY-MM-DD`. Due times use `due_at` (ISO 8601 with timezone).
- Pagination: `offset` token from `next_page.offset`. Check `next_page` for more.
+58
View File
@@ -0,0 +1,58 @@
---
name: attio
version: "1.0.0"
description: Attio API — Attio is a modern CRM platform that offers fully customizable workspaces
activation:
keywords:
- "attio"
- "crm"
patterns:
- "(?i)attio"
tags:
- "crm"
- "sales"
- "contacts"
- "CRM"
max_context_tokens: 1200
---
# Attio API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://api.attio.com/v2`
## Actions
**List records:**
```
http(method="POST", url="https://api.attio.com/v2/objects/{object_slug}/records/query", body={"limit": 20})
```
**Get record:**
```
http(method="GET", url="https://api.attio.com/v2/objects/{object_slug}/records/{record_id}")
```
**Create record:**
```
http(method="POST", url="https://api.attio.com/v2/objects/{object_slug}/records", body={"data": {"values": {"name": [{"value": "Acme Corp"}]}}})
```
**List objects:**
```
http(method="GET", url="https://api.attio.com/v2/objects")
```
**List lists:**
```
http(method="GET", url="https://api.attio.com/v2/lists")
```
## Notes
- Standard objects: `companies`, `people`, `deals`.
- Values are arrays of typed entries: `[{"value": "..."}]`.
- Use `/objects/{slug}/records/query` with filters for searching.
+61
View File
@@ -0,0 +1,61 @@
---
name: auth0-management
version: "1.0.0"
description: Auth0 Management API — Auth0 delivers a flexible, drop-in authentication and authorization platform tha
activation:
keywords:
- "auth0-management"
- "auth0 management"
- "authentication"
patterns:
- "(?i)auth0.?management"
tags:
- "tools"
- "authentication"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [AUTH0_API_TOKEN, AUTH0_MANAGEMENT_TENANT_DOMAIN]
---
# Auth0 Management API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://{AUTH0_DOMAIN}/api/v2`
## Actions
**List users:**
```
http(method="GET", url="https://{AUTH0_DOMAIN}/api/v2/users?page=0&per_page=10")
```
**Get user:**
```
http(method="GET", url="https://{AUTH0_DOMAIN}/api/v2/users/{user_id}")
```
**Create user:**
```
http(method="POST", url="https://{AUTH0_DOMAIN}/api/v2/users", body={"email": "john@example.com","password": "SecureP@ss1","connection": "Username-Password-Authentication"})
```
**Update user:**
```
http(method="PATCH", url="https://{AUTH0_DOMAIN}/api/v2/users/{user_id}", body={"name": "John Doe"})
```
**List roles:**
```
http(method="GET", url="https://{AUTH0_DOMAIN}/api/v2/roles")
```
## Notes
- `user_id` is URL-encoded: `auth0|abc123``auth0%7Cabc123`.
- Connections: `Username-Password-Authentication`, `google-oauth2`, etc.
- Use `q` param for Lucene query syntax search: `?q=email:"*@acme.com"`.
+38
View File
@@ -0,0 +1,38 @@
---
name: autodesk
version: "1.0.0"
description: Autodesk API — Autodesk is a global leader in design and engineering software
activation:
keywords:
- "autodesk"
- "software"
patterns:
- "(?i)autodesk"
tags:
- "software"
- "development"
- "tools"
max_context_tokens: 1200
---
# Autodesk API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
> Autodesk is a global leader in design and engineering software, offering a cloudconnected Design and Make Platform—including AutoCAD, Revit, Fusion 360, and Autodesk Platform Services—that connects d
## Authentication
This integration uses **OAuth 2.0**. The token is managed automatically — no manual auth setup required in API calls.
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: ayrshare
version: "1.0.0"
description: Ayrshare API — Ayrshare provides a unified REST-based social media API that lets developers pro
activation:
keywords:
- "ayrshare"
- "social media"
patterns:
- "(?i)ayrshare"
tags:
- "social-media"
- "social"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [AYRSHARE_API_KEY]
---
# Ayrshare API
Use the `http` tool. API key is automatically injected via `Authorization` header — **never construct auth headers manually**.
> Ayrshare provides a unified REST-based social media API that lets developers programmatically post, schedule, delete, and analyze content across 13 major networks, manage comments and messages, and au
## Authentication
This integration uses **API Key** authentication via the `Authorization` header.
Format: `Authorization: Bearer ...`
## Required Credentials
- `AYRSHARE_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+45
View File
@@ -0,0 +1,45 @@
---
name: basin
version: "1.0.0"
description: Basin API — Basin is a form backend service that captures form submissions from static sites
activation:
keywords:
- "basin"
- "workflow automation"
patterns:
- "(?i)basin"
tags:
- "tools"
- "workflow-automation"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BASIN_API_KEY]
---
# Basin API
Use the `http` tool. API key is automatically injected as `api_token` query parameter.
> Basin is a form backend service that captures form submissions from static sites and forwards data to email, webhooks or integrations—enabling developers to handle form data without building a custom
## Authentication
This integration uses **query parameter** authentication via `api_token`.
## Required Credentials
- `BASIN_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+55
View File
@@ -0,0 +1,55 @@
---
name: beehiiv
version: "1.0.0"
description: Beehiiv API — Beehiiv is a newsletter platform designed for creators and publishers to grow
activation:
keywords:
- "beehiiv"
- "tools"
patterns:
- "(?i)beehiiv"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BEEHIIV_API_KEY]
---
# Beehiiv API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://api.beehiiv.com/v2`
## Actions
**List publications:**
```
http(method="GET", url="https://api.beehiiv.com/v2/publications")
```
**List subscribers:**
```
http(method="GET", url="https://api.beehiiv.com/v2/publications/{pub_id}/subscriptions?limit=10")
```
**Create subscriber:**
```
http(method="POST", url="https://api.beehiiv.com/v2/publications/{pub_id}/subscriptions", body={"email": "reader@example.com","reactivate_existing": true})
```
**List posts:**
```
http(method="GET", url="https://api.beehiiv.com/v2/publications/{pub_id}/posts?status=confirmed&limit=10")
```
## Notes
- Publication IDs start with `pub_`.
- Post status: `draft`, `confirmed` (published), `archived`.
- Subscriber status: `active`, `inactive`, `validating`.
+48
View File
@@ -0,0 +1,48 @@
---
name: benchmark-email
version: "1.0.0"
description: Benchmark Email API — Benchmark Email is an intuitive email-marketing platform that enables marketers
activation:
keywords:
- "benchmark-email"
- "benchmark email"
- "marketing"
patterns:
- "(?i)benchmark.?email"
tags:
- "marketing"
- "email"
- "campaigns"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BENCHMARK_EMAIL_AUTH_TOKEN]
---
# Benchmark Email API
Use the `http` tool. API key is automatically injected via `AuthToken` header — **never construct auth headers manually**.
> Benchmark Email is an intuitive email-marketing platform that enables marketers to design mobile-responsive campaigns with drag-and-drop ease, segment contacts for targeted sends, and track real-time
## Authentication
This integration uses **API Key** authentication via the `AuthToken` header.
Format: `AuthToken: ...`
## Required Credentials
- `BENCHMARK_EMAIL_AUTH_TOKEN` — Auth Token
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+45
View File
@@ -0,0 +1,45 @@
---
name: benzinga
version: "1.0.0"
description: Benzinga API — A real-time financial news and market data service that delivers breaking market
activation:
keywords:
- "benzinga"
- "financial data"
patterns:
- "(?i)benzinga"
tags:
- "tools"
- "financial-data"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BENZINGA_API_KEY]
---
# Benzinga API
Use the `http` tool. API key is automatically injected as `token` query parameter.
> A real-time financial news and market data service that delivers breaking market updates, analysis, earnings, and trading insights to investors, traders, and finance professionals.
## Authentication
This integration uses **query parameter** authentication via `token`.
## Required Credentials
- `BENZINGA_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+47
View File
@@ -0,0 +1,47 @@
---
name: better-proposals
version: "1.0.0"
description: Better Proposals API — A web-based platform that enables businesses to create, customize, send
activation:
keywords:
- "better-proposals"
- "better proposals"
- "proposal software"
patterns:
- "(?i)better.?proposals"
tags:
- "tools"
- "proposal-software"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BETTER_PROPOSALS_API_TOKEN]
---
# Better Proposals API
Use the `http` tool. API key is automatically injected via `Bptoken` header — **never construct auth headers manually**.
> A web-based platform that enables businesses to create, customize, send, and track professional sales proposals and contracts with interactive elements, e-signatures, and analytics to streamline clien
## Authentication
This integration uses **API Key** authentication via the `Bptoken` header.
Format: `Bptoken: ...`
## Required Credentials
- `BETTER_PROPOSALS_API_TOKEN` — API Token
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+61
View File
@@ -0,0 +1,61 @@
---
name: big-commerce
version: "1.0.0"
description: BigCommerce API — BigCommerce is an open SaaS eCommerce platform that enables businesses to build
activation:
keywords:
- "big-commerce"
- "bigcommerce"
- "ecommerce"
patterns:
- "(?i)big.?commerce"
tags:
- "tools"
- "ecommerce"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BIG_COMMERCE_STORE_HASH, BIG_COMMERCE_ACCESS_TOKEN]
---
# BigCommerce API
Use the `http` tool. API key is automatically injected via `X-Auth-Token` header — **never construct auth headers manually**.
## Base URL
`https://api.bigcommerce.com/stores/{BIGCOMMERCE_STORE_HASH}/v3`
## Actions
**List products:**
```
http(method="GET", url="https://api.bigcommerce.com/stores/{BIGCOMMERCE_STORE_HASH}/v3/catalog/products?limit=10")
```
**Get product:**
```
http(method="GET", url="https://api.bigcommerce.com/stores/{BIGCOMMERCE_STORE_HASH}/v3/catalog/products/{product_id}")
```
**Create product:**
```
http(method="POST", url="https://api.bigcommerce.com/stores/{BIGCOMMERCE_STORE_HASH}/v3/catalog/products", body={"name": "Widget","type": "physical","weight": 1.0,"price": 29.99})
```
**List orders:**
```
http(method="GET", url="https://api.bigcommerce.com/stores/{BIGCOMMERCE_STORE_HASH}/v3/orders?limit=10")
```
**List customers:**
```
http(method="GET", url="https://api.bigcommerce.com/stores/{BIGCOMMERCE_STORE_HASH}/v3/customers?limit=10")
```
## Notes
- Auth via `X-Auth-Token` header (auto-injected).
- Products have `type`: `physical`, `digital`.
- Prices are decimals, weights in the store's configured unit.
+53
View File
@@ -0,0 +1,53 @@
---
name: bigquery
version: "1.0.0"
description: BigQuery API — BigQuery is a serverless, highly scalable
activation:
keywords:
- "bigquery"
- "ai"
patterns:
- "(?i)bigquery"
tags:
- "ai"
- "machine-learning"
- "storage"
max_context_tokens: 1200
---
# BigQuery API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://bigquery.googleapis.com/bigquery/v2`
## Actions
**List datasets:**
```
http(method="GET", url="https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/datasets")
```
**List tables:**
```
http(method="GET", url="https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/datasets/{dataset_id}/tables")
```
**Run query:**
```
http(method="POST", url="https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/queries", body={"query": "SELECT * FROM `project.dataset.table` LIMIT 10","useLegacySql": false})
```
**Get query results:**
```
http(method="GET", url="https://bigquery.googleapis.com/bigquery/v2/projects/{project_id}/queries/{job_id}")
```
## Notes
- Uses OAuth 2.0 — credentials are auto-injected.
- Queries use Standard SQL by default (`useLegacySql: false`).
- Large results: check `jobComplete` field; poll with `getQueryResults` if `false`.
- Table references: `project.dataset.table`.
+47
View File
@@ -0,0 +1,47 @@
---
name: blaze-meter-functional
version: "1.0.0"
description: BlazeMeter Functional API — BlazeMeter is a testing platform for web and API applications
activation:
keywords:
- "blaze-meter-functional"
- "blazemeter functional"
- "developer tool"
patterns:
- "(?i)blaze.?meter.?functional"
tags:
- "tools"
- "developer-tool"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BLAZE_METER_FUNCTIONAL_API_KEY, BLAZE_METER_FUNCTIONAL_API_SECRET, BLAZE_METER_ACCOUNT_ID]
---
# BlazeMeter Functional API
Use the `http` tool. Credentials are automatically injected.
> BlazeMeter is a testing platform for web and API applications, and its Functional API enables developers to programmatically create, manage, and run automated API tests to validate responses, workflow
## Authentication
## Required Credentials
- `BLAZE_METER_FUNCTIONAL_API_KEY` — API Key
- `BLAZE_METER_FUNCTIONAL_API_SECRET` — API Secret
- `BLAZE_METER_ACCOUNT_ID` — Account ID
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: blaze-meter-performance
version: "1.0.0"
description: BlazeMeter Performance API — BlazeMeter is a testing platform for web and API applications
activation:
keywords:
- "blaze-meter-performance"
- "blazemeter performance"
- "developer tool"
patterns:
- "(?i)blaze.?meter.?performance"
tags:
- "tools"
- "developer-tool"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BLAZE_METER_PERFORMANCE_API_KEY, BLAZE_METER_PERFORMANCE_API_SECRET]
---
# BlazeMeter Performance API
Use the `http` tool. Credentials are automatically injected.
> BlazeMeter is a testing platform for web and API applications, and its Performance API enables developers to programmatically create, configure, run, and retrieve results from large-scale performance
## Authentication
## Required Credentials
- `BLAZE_METER_PERFORMANCE_API_KEY` — API Key
- `BLAZE_METER_PERFORMANCE_API_SECRET` — API Secret
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
@@ -0,0 +1,46 @@
---
name: blaze-meter-service-virtualization
version: "1.0.0"
description: BlazeMeter Service Virtualization API — BlazeMeter is a testing platform for web and API applications
activation:
keywords:
- "blaze-meter-service-virtualization"
- "blazemeter service virtualization"
- "developer tool"
patterns:
- "(?i)blaze.?meter.?service.?virtualization"
tags:
- "tools"
- "developer-tool"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BLAZE_METER_SERVICE_VIRTUALIZATION_API_KEY, BLAZE_METER_SERVICE_VIRTUALIZATION_API_SECRET]
---
# BlazeMeter Service Virtualization API
Use the `http` tool. Credentials are automatically injected.
> BlazeMeter is a testing platform for web and API applications, and its Service Virtualization API enables developers to programmatically create and manage virtual services that simulate APIs and syste
## Authentication
## Required Credentials
- `BLAZE_METER_SERVICE_VIRTUALIZATION_API_KEY` — API Key
- `BLAZE_METER_SERVICE_VIRTUALIZATION_API_SECRET` — API Secret
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+38
View File
@@ -0,0 +1,38 @@
---
name: bluebeam
version: "1.0.0"
description: Bluebeam API — Bluebeam is a PDF-centric collaboration platform tailored for architecture
activation:
keywords:
- "bluebeam"
- "software"
patterns:
- "(?i)bluebeam"
tags:
- "software"
- "development"
- "tools"
max_context_tokens: 1200
---
# Bluebeam API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
> Bluebeam is a PDF-centric collaboration platform tailored for architecture, engineering, and construction professionals, offering industry-grade markup, measurement, takeoff, and document management t
## Authentication
This integration uses **OAuth 2.0**. The token is managed automatically — no manual auth setup required in API calls.
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+61
View File
@@ -0,0 +1,61 @@
---
name: bluesky
version: "1.0.0"
description: Bluesky API — Bluesky is a decentralized social media platform built on the open-source AT Pro
activation:
keywords:
- "bluesky"
- "tools"
patterns:
- "(?i)bluesky"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BLUESKY_ACCESS_TOKEN, BLUESKY_ENTRYWAY]
---
# Bluesky API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://bsky.social/xrpc`
## Actions
**Create session (login):**
```
http(method="POST", url="https://bsky.social/xrpc/com.atproto.server.createSession", body={"identifier": "your.handle","password": "app-password"})
```
**Create post:**
```
http(method="POST", url="https://bsky.social/xrpc/com.atproto.repo.createRecord", body={"repo": "did:plc:xxx","collection": "app.bsky.feed.post","record": {"text": "Hello Bluesky!","$type": "app.bsky.feed.post","createdAt": "2026-03-27T00:00:00Z"}})
```
**Get profile:**
```
http(method="GET", url="https://bsky.social/xrpc/app.bsky.actor.getProfile?actor=your.handle")
```
**Get timeline:**
```
http(method="GET", url="https://bsky.social/xrpc/app.bsky.feed.getTimeline?limit=20")
```
**Search posts:**
```
http(method="GET", url="https://bsky.social/xrpc/app.bsky.feed.searchPosts?q=search+term&limit=10")
```
## Notes
- First create a session to get an access token.
- Posts are AT Protocol records in collection `app.bsky.feed.post`.
- Handles look like `username.bsky.social` or custom domains.
- DIDs are persistent identifiers: `did:plc:xxxxx`.
+45
View File
@@ -0,0 +1,45 @@
---
name: bookingmood
version: "1.0.0"
description: Bookingmood API — A flexible booking platform that lets rental and vacation property owners embed
activation:
keywords:
- "bookingmood"
- "booking software"
patterns:
- "(?i)bookingmood"
tags:
- "tools"
- "booking-software"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BOOKINGMOOD_API_KEY]
---
# Bookingmood API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
> A flexible booking platform that lets rental and vacation property owners embed customizable calendars on their websites, manage availability and reservations, track payments, sync with external calen
## Authentication
This integration uses **Bearer Token** authentication. The token is injected automatically into the `Authorization` header.
## Required Credentials
- `BOOKINGMOOD_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: bot-star
version: "1.0.0"
description: BotStar API — A visual bot-building platform that enables businesses to design, deploy
activation:
keywords:
- "bot-star"
- "botstar"
- "chatbot builder"
patterns:
- "(?i)bot.?star"
tags:
- "tools"
- "chatbot-builder"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BOT_STAR_API_TOKEN]
---
# BotStar API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
> A visual bot-building platform that enables businesses to design, deploy, and manage AI-powered chatbots for websites, messaging apps, and customer support workflows without coding.
## Authentication
This integration uses **Bearer Token** authentication. The token is injected automatically into the `Authorization` header.
## Required Credentials
- `BOT_STAR_API_TOKEN` — API Token
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: bouncer
version: "1.0.0"
description: Bouncer API — A cloud-based platform that verifies and cleans email lists by identifying inval
activation:
keywords:
- "bouncer"
- "email verification"
patterns:
- "(?i)bouncer"
tags:
- "tools"
- "email-verification"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BOUNCER_API_KEY]
---
# Bouncer API
Use the `http` tool. API key is automatically injected via `x-api-key` header — **never construct auth headers manually**.
> A cloud-based platform that verifies and cleans email lists by identifying invalid, risky, or disposable email addresses to improve deliverability, reduce bounce rates, and enhance email campaign perf
## Authentication
This integration uses **API Key** authentication via the `x-api-key` header.
Format: `x-api-key: ...`
## Required Credentials
- `BOUNCER_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: box-hero
version: "1.0.0"
description: BoxHero API — A cloud-based inventory and stock management platform that enables businesses to
activation:
keywords:
- "box-hero"
- "boxhero"
- "inventory management"
patterns:
- "(?i)box.?hero"
tags:
- "tools"
- "inventory-management"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BOX_HERO_API_TOKEN]
---
# BoxHero API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
> A cloud-based inventory and stock management platform that enables businesses to track products, manage warehouses, monitor stock levels in real time, and automate order fulfillment across sales chann
## Authentication
This integration uses **Bearer Token** authentication. The token is injected automatically into the `Authorization` header.
## Required Credentials
- `BOX_HERO_API_TOKEN` — API Token
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+84
View File
@@ -0,0 +1,84 @@
---
name: box
version: "1.0.0"
description: Box API — files, folders, collaborations, search, comments
activation:
keywords:
- "box"
- "box.com"
- "box file"
exclude_keywords:
- "dropbox"
- "google drive"
patterns:
- "(?i)box\\.com.*(file|folder|share)"
- "(?i)(upload|download|share).*box"
tags:
- "file-storage"
- "cloud-storage"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BOX_ACCESS_TOKEN]
---
# Box Content API
Use the `http` tool. Credentials are automatically injected for `api.box.com`.
## Base URL
`https://api.box.com/2.0`
## Actions
**Get folder items:**
```
http(method="GET", url="https://api.box.com/2.0/folders/<folder_id>/items?fields=id,name,type,modified_at,size&limit=20")
```
Root folder ID is `"0"`.
**Get file info:**
```
http(method="GET", url="https://api.box.com/2.0/files/<file_id>?fields=id,name,size,modified_at,shared_link")
```
**Search:**
```
http(method="GET", url="https://api.box.com/2.0/search?query=report&type=file&limit=20")
```
**Create folder:**
```
http(method="POST", url="https://api.box.com/2.0/folders", body={"name": "New Folder", "parent": {"id": "0"}})
```
**Copy file:**
```
http(method="POST", url="https://api.box.com/2.0/files/<file_id>/copy", body={"parent": {"id": "<folder_id>"}})
```
**Create shared link:**
```
http(method="PUT", url="https://api.box.com/2.0/files/<file_id>?fields=shared_link", body={"shared_link": {"access": "open"}})
```
**Add collaboration:**
```
http(method="POST", url="https://api.box.com/2.0/collaborations", body={"item": {"type": "folder", "id": "<folder_id>"}, "accessible_by": {"type": "user", "login": "user@example.com"}, "role": "editor"})
```
**Add comment:**
```
http(method="POST", url="https://api.box.com/2.0/comments", body={"item": {"type": "file", "id": "<file_id>"}, "message": "Please review"})
```
## Notes
- Root folder is always `"0"`.
- Collaboration roles: `editor`, `viewer`, `previewer`, `uploader`, `co-owner`.
- Shared link access: `open` (anyone), `company` (organization), `collaborators` (invited only).
- Use `fields` param to request specific properties.
- Pagination: `offset` + `limit`. Check `total_count`.
- File uploads use `https://upload.box.com/api/2.0/files/content` (different host).
+46
View File
@@ -0,0 +1,46 @@
---
name: breathe
version: "1.0.0"
description: Breathe API — Breathe is a cloud-based HR software designed for small and medium-sized busines
activation:
keywords:
- "breathe"
- "hris"
patterns:
- "(?i)breathe"
tags:
- "tools"
- "hris"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BREATHE_API_KEY]
---
# Breathe API
Use the `http` tool. API key is automatically injected via `X-API-KEY` header — **never construct auth headers manually**.
> Breathe is a cloud-based HR software designed for small and medium-sized businesses, providing tools for managing employee records, leave requests, performance reviews, document storage, and reporting
## Authentication
This integration uses **API Key** authentication via the `X-API-KEY` header.
Format: `X-API-KEY: ...`
## Required Credentials
- `BREATHE_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+76
View File
@@ -0,0 +1,76 @@
---
name: brevo
version: "1.0.0"
description: Brevo (Sendinblue) API — email campaigns, contacts, transactional email, SMS
activation:
keywords:
- "brevo"
- "sendinblue"
- "email campaign"
exclude_keywords:
- "mailchimp"
- "resend"
patterns:
- "(?i)(brevo|sendinblue).*(email|contact|campaign|sms)"
tags:
- "email"
- "marketing"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BREVO_API_KEY]
---
# Brevo API (formerly Sendinblue)
Use the `http` tool. Include `api-key` header.
## Base URL
`https://api.brevo.com/v3`
## Actions
**Send transactional email:**
```
http(method="POST", url="https://api.brevo.com/v3/smtp/email", headers=[{"name": "api-key", "value": "{BREVO_API_KEY}"}], body={"sender": {"name": "My App", "email": "noreply@yourdomain.com"}, "to": [{"email": "recipient@example.com", "name": "John"}], "subject": "Welcome!", "htmlContent": "<h1>Hello John</h1><p>Welcome aboard.</p>"})
```
**List contacts:**
```
http(method="GET", url="https://api.brevo.com/v3/contacts?limit=20&offset=0", headers=[{"name": "api-key", "value": "{BREVO_API_KEY}"}])
```
**Create contact:**
```
http(method="POST", url="https://api.brevo.com/v3/contacts", headers=[{"name": "api-key", "value": "{BREVO_API_KEY}"}], body={"email": "new@example.com", "attributes": {"FIRSTNAME": "Alice", "LASTNAME": "Smith"}, "listIds": [1]})
```
**Update contact:**
```
http(method="PUT", url="https://api.brevo.com/v3/contacts/user@example.com", headers=[{"name": "api-key", "value": "{BREVO_API_KEY}"}], body={"attributes": {"FIRSTNAME": "Updated"}})
```
**List email campaigns:**
```
http(method="GET", url="https://api.brevo.com/v3/emailCampaigns?limit=20&status=sent", headers=[{"name": "api-key", "value": "{BREVO_API_KEY}"}])
```
**Get campaign stats:**
```
http(method="GET", url="https://api.brevo.com/v3/emailCampaigns/<campaign_id>", headers=[{"name": "api-key", "value": "{BREVO_API_KEY}"}])
```
**Send SMS:**
```
http(method="POST", url="https://api.brevo.com/v3/transactionalSMS/sms", headers=[{"name": "api-key", "value": "{BREVO_API_KEY}"}], body={"sender": "MyApp", "recipient": "+1234567890", "content": "Your code is 123456", "type": "transactional"})
```
## Notes
- Contact attributes are uppercase: `FIRSTNAME`, `LASTNAME`, `SMS`.
- `listIds` are integer IDs of contact lists.
- Campaign statuses: `draft`, `sent`, `queued`, `suspended`, `in_process`.
- Pagination: `limit` + `offset`. Check `count` for total.
- Transactional email returns `messageId` for tracking.
+37
View File
@@ -0,0 +1,37 @@
---
name: brex
version: "1.0.0"
description: Brex API — Brex combines corporate cards, business accounts, expense management, bill pay
activation:
keywords:
- "brex"
- "finance"
patterns:
- "(?i)brex"
tags:
- "tools"
- "finance"
max_context_tokens: 1200
---
# Brex API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
> Brex combines corporate cards, business accounts, expense management, bill pay, and travel booking into a single AI-powered financial operations platform—offering unified spend control, real-time visi
## Authentication
This integration uses **OAuth 2.0**. The token is managed automatically — no manual auth setup required in API calls.
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: bright-data
version: "1.0.0"
description: BrightData API — Bright Data (formerly Luminati) is a comprehensive web data platform offering a
activation:
keywords:
- "bright-data"
- "brightdata"
- "tools"
patterns:
- "(?i)bright.?data"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BRIGHT_DATA_API_KEY]
---
# BrightData API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
> Bright Data (formerly Luminati) is a comprehensive web data platform offering a global proxy network (residential, mobile, ISP, data center), browser-based scraping, SERP APIs, and managed data pipeli
## Authentication
This integration uses **Bearer Token** authentication. The token is injected automatically into the `Authorization` header.
## Required Credentials
- `BRIGHT_DATA_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: browse-ai
version: "1.0.0"
description: Browse AI API — Browse AI is a no-code, AI-powered web scraping and monitoring platform that ena
activation:
keywords:
- "browse-ai"
- "browse ai"
- "scraper"
patterns:
- "(?i)browse.?ai"
tags:
- "tools"
- "scraper"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BROWSE_AI_API_KEY]
---
# Browse AI API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
> Browse AI is a no-code, AI-powered web scraping and monitoring platform that enables users to extract structured data from any website, set automated alerts for changes, and funnel the output into spr
## Authentication
This integration uses **Bearer Token** authentication. The token is injected automatically into the `Authorization` header.
## Required Credentials
- `BROWSE_AI_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+51
View File
@@ -0,0 +1,51 @@
---
name: browserbase
version: "1.0.0"
description: Browserbase API — Browserbase is a cloud platform that lets developers run headless browsers at sc
activation:
keywords:
- "browserbase"
- "ai"
patterns:
- "(?i)browserbase"
tags:
- "ai"
- "machine-learning"
- "storage"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BROWSERBASE_API_KEY]
---
# Browserbase API
Use the `http` tool. API key is automatically injected via `X-BB-API-Key` header — **never construct auth headers manually**.
## Base URL
`https://api.browserbase.com/v1`
## Actions
**Create session:**
```
http(method="POST", url="https://api.browserbase.com/v1/sessions", body={"projectId": "<project_id>"})
```
**List sessions:**
```
http(method="GET", url="https://api.browserbase.com/v1/sessions?limit=10")
```
**Get session:**
```
http(method="GET", url="https://api.browserbase.com/v1/sessions/{session_id}")
```
## Notes
- Sessions provide a headless browser instance.
- Connect via CDP (Chrome DevTools Protocol) using the session's debug URL.
- Project ID is required for session creation.
+47
View File
@@ -0,0 +1,47 @@
---
name: bug-bug
version: "1.0.0"
description: BugBug API — A no-code testing platform that automatically crawls websites and web apps to de
activation:
keywords:
- "bug-bug"
- "bugbug"
- "bug detection"
patterns:
- "(?i)bug.?bug"
tags:
- "tools"
- "bug-detection"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BUG_BUG_API_KEY]
---
# BugBug API
Use the `http` tool. API key is automatically injected via `Authorization` header — **never construct auth headers manually**.
> A no-code testing platform that automatically crawls websites and web apps to detect visual issues, broken links, and UI regressions, helping teams maintain quality, catch bugs early, and streamline Q
## Authentication
This integration uses **API Key** authentication via the `Authorization` header.
Format: `Authorization: Token ...`
## Required Credentials
- `BUG_BUG_API_KEY` — API key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: bug-herd
version: "1.0.0"
description: BugHerd API — A web-based issue and feedback tracking tool that lets teams capture visual bugs
activation:
keywords:
- "bug-herd"
- "bugherd"
- "bug tracking"
patterns:
- "(?i)bug.?herd"
tags:
- "tools"
- "bug-tracking"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BUG_HERD_USERNAME, BUG_HERD_PASSWORD]
---
# BugHerd API
Use the `http` tool. Credentials are automatically injected.
> A web-based issue and feedback tracking tool that lets teams capture visual bugs directly on web pages, annotate problems, manage tickets, and collaborate on fixes for faster design and development cy
## Authentication
## Required Credentials
- `BUG_HERD_USERNAME` — API Key
- `BUG_HERD_PASSWORD` — Password
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+47
View File
@@ -0,0 +1,47 @@
---
name: byte-forms
version: "1.0.0"
description: ByteForms API — A no-code form creation tool that enables businesses to design, deploy
activation:
keywords:
- "byte-forms"
- "byteforms"
- "form builder"
patterns:
- "(?i)byte.?forms"
tags:
- "tools"
- "form-builder"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [BYTE_FORMS_API_KEY]
---
# ByteForms API
Use the `http` tool. API key is automatically injected via `Authorization` header — **never construct auth headers manually**.
> A no-code form creation tool that enables businesses to design, deploy, and embed customizable online forms for data collection, surveys, and customer feedback with optional logic and integrations.
## Authentication
This integration uses **API Key** authentication via the `Authorization` header.
Format: `Authorization: ...`
## Required Credentials
- `BYTE_FORMS_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+81
View File
@@ -0,0 +1,81 @@
---
name: calcom
version: "1.0.0"
description: Cal.com API v2 — event types, bookings, availability, schedules
activation:
keywords:
- "cal.com"
- "calcom"
- "cal dot com"
exclude_keywords:
- "calendly"
- "google calendar"
patterns:
- "(?i)cal\\.?com.*(event|booking|availability|schedule)"
tags:
- "scheduling"
- "calendar"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CALCOM_API_KEY]
---
# Cal.com API v2
Use the `http` tool. Credentials are automatically injected for `api.cal.com`.
## Base URL
`https://api.cal.com/v2`
## Actions
**List event types:**
```
http(method="GET", url="https://api.cal.com/v2/event-types")
```
**Get event type:**
```
http(method="GET", url="https://api.cal.com/v2/event-types/<event_type_id>")
```
**List bookings:**
```
http(method="GET", url="https://api.cal.com/v2/bookings?status=upcoming&take=20")
```
**Get booking:**
```
http(method="GET", url="https://api.cal.com/v2/bookings/<booking_uid>")
```
**Create booking:**
```
http(method="POST", url="https://api.cal.com/v2/bookings", body={"eventTypeId": 123, "start": "2026-04-01T10:00:00Z", "attendee": {"name": "John Doe", "email": "john@example.com", "timeZone": "America/New_York"}, "metadata": {}})
```
**Cancel booking:**
```
http(method="POST", url="https://api.cal.com/v2/bookings/<booking_uid>/cancel", body={"cancellationReason": "Schedule conflict"})
```
**Get availability:**
```
http(method="GET", url="https://api.cal.com/v2/slots/available?startTime=2026-04-01T00:00:00Z&endTime=2026-04-07T00:00:00Z&eventTypeId=123")
```
**List schedules:**
```
http(method="GET", url="https://api.cal.com/v2/schedules")
```
## Notes
- Responses: `{"status": "success", "data": {...}}`.
- Booking status: `upcoming`, `recurring`, `past`, `cancelled`, `unconfirmed`.
- Times are always UTC (ISO 8601). Attendee specifies their timezone.
- Booking UIDs are UUID strings.
- Use `take` and `skip` for pagination.
+77
View File
@@ -0,0 +1,77 @@
---
name: calendly
version: "1.0.0"
description: Calendly API v2 — event types, scheduled events, invitees
activation:
keywords:
- "calendly"
- "calendly event"
- "booking"
- "scheduling link"
exclude_keywords:
- "cal.com"
- "google calendar"
patterns:
- "(?i)calendly.*(event|booking|invitee|schedule)"
tags:
- "scheduling"
- "calendar"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CALENDLY_ACCESS_TOKEN]
---
# Calendly API v2
Use the `http` tool. Credentials are automatically injected for `api.calendly.com`.
## Base URL
`https://api.calendly.com`
## Actions
**Get current user:**
```
http(method="GET", url="https://api.calendly.com/users/me")
```
**List event types:**
```
http(method="GET", url="https://api.calendly.com/event_types?user=<user_uri>&count=20")
```
**List scheduled events:**
```
http(method="GET", url="https://api.calendly.com/scheduled_events?user=<user_uri>&min_start_time=2026-03-27T00:00:00Z&max_start_time=2026-04-30T00:00:00Z&status=active&count=20")
```
**Get event details:**
```
http(method="GET", url="https://api.calendly.com/scheduled_events/<event_uuid>")
```
**List invitees for event:**
```
http(method="GET", url="https://api.calendly.com/scheduled_events/<event_uuid>/invitees?count=20")
```
**Cancel event:**
```
http(method="POST", url="https://api.calendly.com/scheduled_events/<event_uuid>/cancellation", body={"reason": "Schedule conflict"})
```
**List organization members:**
```
http(method="GET", url="https://api.calendly.com/organization_memberships?organization=<org_uri>")
```
## Notes
- Resources are identified by URIs (e.g., `https://api.calendly.com/users/ABCDEF123456`), not plain IDs.
- First call `GET /users/me` to get your `uri` and `current_organization`.
- Event status: `active`, `canceled`.
- Pagination: use `page_token` from `pagination.next_page_token`.
- Dates are ISO 8601 with timezone (UTC recommended).
+51
View File
@@ -0,0 +1,51 @@
---
name: canva
version: "1.0.0"
description: Canva API — Canva is a cloud-based graphic design platform with a draganddrop editor
activation:
keywords:
- "canva"
- "tools"
patterns:
- "(?i)canva"
tags:
- "tools"
- "utility"
max_context_tokens: 1200
---
# Canva API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://api.canva.com/rest/v1`
## Actions
**List designs:**
```
http(method="GET", url="https://api.canva.com/rest/v1/designs?limit=10")
```
**Get design:**
```
http(method="GET", url="https://api.canva.com/rest/v1/designs/{design_id}")
```
**Create design:**
```
http(method="POST", url="https://api.canva.com/rest/v1/designs", body={"title": "My Design","design_type": {"type": "preset","name": "doc"}})
```
**Export design:**
```
http(method="POST", url="https://api.canva.com/rest/v1/designs/{design_id}/exports", body={"format": {"type": "png"}})
```
## Notes
- Uses OAuth 2.0 — credentials are auto-injected.
- Design types: `doc`, `presentation`, `whiteboard`, `social_media`.
- Export formats: `png`, `jpg`, `pdf`, `svg`, `mp4`.
+39
View File
@@ -0,0 +1,39 @@
---
name: capsule
version: "1.0.0"
description: Capsule API — Capsule CRM is a streamlined cloud-based CRM designed for small businesses and s
activation:
keywords:
- "capsule"
- "crm"
patterns:
- "(?i)capsule"
tags:
- "crm"
- "sales"
- "contacts"
- "CRM"
max_context_tokens: 1200
---
# Capsule API
Use the `http` tool. OAuth credentials are automatically injected — **never construct Authorization headers manually**.
> Capsule CRM is a streamlined cloud-based CRM designed for small businesses and sales teams, furnishing contact and organisation management, sales pipelines, tasks, projects, and analytics in one place
## Authentication
This integration uses **OAuth 2.0**. The token is managed automatically — no manual auth setup required in API calls.
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+46
View File
@@ -0,0 +1,46 @@
---
name: cats
version: "1.0.0"
description: CATS API — CATS is a web-based applicant tracking system designed for recruiting agencies a
activation:
keywords:
- "cats"
- "ats"
patterns:
- "(?i)cats"
tags:
- "tools"
- "ats"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CATS_API_KEY]
---
# CATS API
Use the `http` tool. API key is automatically injected via `authorization` header — **never construct auth headers manually**.
> CATS is a web-based applicant tracking system designed for recruiting agencies and HR teams, offering tools for job posting, resume parsing, candidate tracking, custom workflows, analytics, and integr
## Authentication
This integration uses **API Key** authentication via the `authorization` header.
Format: `authorization: Token ...`
## Required Credentials
- `CATS_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+49
View File
@@ -0,0 +1,49 @@
---
name: central-station-crm
version: "1.0.0"
description: Central Station CRM API — CentralStationCRM is a lightweight CRM designed for small businesses to manage c
activation:
keywords:
- "central-station-crm"
- "central station crm"
- "crm"
patterns:
- "(?i)central.?station.?crm"
tags:
- "crm"
- "sales"
- "contacts"
- "CRM"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CENTRAL_STATION_CRM_API_KEY]
---
# Central Station CRM API
Use the `http` tool. API key is automatically injected via `X-apikey` header — **never construct auth headers manually**.
> CentralStationCRM is a lightweight CRM designed for small businesses to manage contacts, track deals, organize tasks and streamline customer relationships with a simple, user-friendly interface.
## Authentication
This integration uses **API Key** authentication via the `X-apikey` header.
Format: `X-apikey: ...`
## Required Credentials
- `CENTRAL_STATION_CRM_API_KEY` — API Key
## Usage
Use the `http` tool to call this API. Example:
```
http(method="GET", url="<api_base_url>/endpoint")
```
## Common Mistakes
- Do NOT add Authorization headers — automatically injected by the credential system.
- Always use HTTPS URLs.
+65
View File
@@ -0,0 +1,65 @@
---
name: chargebee
version: "1.0.0"
description: Chargebee API — Chargebee is a subscription billing and revenue operations platform that helps S
activation:
keywords:
- "chargebee"
- "crm"
patterns:
- "(?i)chargebee"
tags:
- "crm"
- "sales"
- "contacts"
- "CRM"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CHARGEBEE_SUBDOMAIN, CHARGEBEE_API_KEY]
---
# Chargebee API
Use the `http` tool. Credentials are automatically injected.
## Base URL
`https://{CHARGEBEE_SITE}.chargebee.com/api/v2`
**Content-Type**: `application/x-www-form-urlencoded` for POST/PUT requests.
## Actions
**List subscriptions:**
```
http(method="GET", url="https://{CHARGEBEE_SITE}.chargebee.com/api/v2/subscriptions?limit=10")
```
**Get subscription:**
```
http(method="GET", url="https://{CHARGEBEE_SITE}.chargebee.com/api/v2/subscriptions/{subscription_id}")
```
**Create subscription:**
```
http(method="POST", url="https://{CHARGEBEE_SITE}.chargebee.com/api/v2/subscriptions", headers=[{"name": "Content-Type", "value": "application/x-www-form-urlencoded"}], body="customer[email]=john@example.com&plan_id=basic-monthly")
```
**List customers:**
```
http(method="GET", url="https://{CHARGEBEE_SITE}.chargebee.com/api/v2/customers?limit=10")
```
**List invoices:**
```
http(method="GET", url="https://{CHARGEBEE_SITE}.chargebee.com/api/v2/invoices?limit=10&sort_by[asc]=date")
```
## Notes
- Uses Basic auth with API key as username and empty password.
- POST bodies are form-encoded with bracket notation: `customer[email]=...`.
- Subscription states: `future`, `in_trial`, `active`, `non_renewing`, `paused`, `cancelled`.
- Pagination: `offset` key in response; pass as `?offset=...` for next page.
+63
View File
@@ -0,0 +1,63 @@
---
name: circle-ci
version: "1.0.0"
description: Circle CI API — CircleCI is a continuous integration and delivery (CI/CD) platform that automate
activation:
keywords:
- "circle-ci"
- "circle ci"
- "devops"
patterns:
- "(?i)circle.?ci"
tags:
- "devops"
- "ci-cd"
- "deployment"
- "dev-ops"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CIRCLE_CI_API_KEY]
---
# Circle CI API
Use the `http` tool. API key is automatically injected via `Circle-Token` header — **never construct auth headers manually**.
## Base URL
`https://circleci.com/api/v2`
## Actions
**Get current user:**
```
http(method="GET", url="https://circleci.com/api/v2/me")
```
**List pipelines:**
```
http(method="GET", url="https://circleci.com/api/v2/project/{project_slug}/pipeline?branch=main")
```
**Get pipeline:**
```
http(method="GET", url="https://circleci.com/api/v2/pipeline/{pipeline_id}")
```
**List workflows:**
```
http(method="GET", url="https://circleci.com/api/v2/pipeline/{pipeline_id}/workflow")
```
**Trigger pipeline:**
```
http(method="POST", url="https://circleci.com/api/v2/project/{project_slug}/pipeline", body={"branch": "main","parameters": {}})
```
## Notes
- Project slug format: `gh/{org}/{repo}` or `bb/{org}/{repo}`.
- Pipeline statuses: `created`, `errored`, `setup-pending`, `setup`, `pending`.
- Workflow statuses: `success`, `running`, `not_run`, `failed`, `error`, `failing`, `on_hold`, `canceled`.
+68
View File
@@ -0,0 +1,68 @@
---
name: clerk
version: "1.0.0"
description: Clerk API — Clerk provides user authentication and user management solutions for modern web
activation:
keywords:
- "clerk"
- "crm"
patterns:
- "(?i)clerk"
tags:
- "crm"
- "sales"
- "contacts"
- "CRM"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CLERK_API_KEY]
---
# Clerk API
Use the `http` tool. Credentials are automatically injected — **never construct Authorization headers manually**.
## Base URL
`https://api.clerk.com/v1`
## Actions
**List users:**
```
http(method="GET", url="https://api.clerk.com/v1/users?limit=10&order_by=-created_at")
```
**Get user:**
```
http(method="GET", url="https://api.clerk.com/v1/users/{user_id}")
```
**Create user:**
```
http(method="POST", url="https://api.clerk.com/v1/users", body={"email_address": ["john@example.com"],"first_name": "John","last_name": "Doe","password": "SecureP@ss1"})
```
**Update user:**
```
http(method="PATCH", url="https://api.clerk.com/v1/users/{user_id}", body={"first_name": "Jane"})
```
**Delete user:**
```
http(method="DELETE", url="https://api.clerk.com/v1/users/{user_id}")
```
**List organizations:**
```
http(method="GET", url="https://api.clerk.com/v1/organizations?limit=10")
```
## Notes
- User IDs start with `user_`.
- Email addresses are arrays — users can have multiple.
- Use `?query=` for fuzzy search across name/email.
- Pagination: `limit` + `offset`.
+52
View File
@@ -0,0 +1,52 @@
---
name: click-house
version: "1.0.0"
description: ClickHouse API — ClickHouse is an ultra-fast, column-oriented database designed for real-time ana
activation:
keywords:
- "click-house"
- "clickhouse"
- "analytics"
patterns:
- "(?i)click.?house"
tags:
- "analytics"
- "data"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CLICK_HOUSE_API_KEY, CLICK_HOUSE_SECRET_KEY]
---
# ClickHouse API
Use the `http` tool. Credentials are automatically injected.
## Base URL
`https://{CLICKHOUSE_HOST}:8443`
## Actions
**Run query:**
```
http(method="POST", url="https://{CLICKHOUSE_HOST}:8443/?query=SELECT+1")
```
**List tables:**
```
http(method="POST", url="https://{CLICKHOUSE_HOST}:8443/?query=SHOW+TABLES+FROM+default")
```
**Insert data:**
```
http(method="POST", url="https://{CLICKHOUSE_HOST}:8443/?query=INSERT+INTO+table+FORMAT+JSONEachRow", body=[{"col1": "value1","col2": 42}])
```
## Notes
- ClickHouse uses HTTP interface on port 8443 (HTTPS) or 8123 (HTTP).
- Pass SQL in `query` parameter or POST body.
- Output formats: `JSON`, `JSONEachRow`, `CSV`, `TSV`.
- Auth: Basic auth or `X-ClickHouse-User`/`X-ClickHouse-Key` headers.
+52
View File
@@ -0,0 +1,52 @@
---
name: click-send
version: "1.0.0"
description: ClickSend API — A cloud-based messaging and communication service that enables businesses to sen
activation:
keywords:
- "click-send"
- "clicksend"
- "communication"
patterns:
- "(?i)click.?send"
tags:
- "messaging"
- "communication"
- "chat"
max_context_tokens: 1200
metadata:
openclaw:
requires:
env: [CLICK_SEND_USERNAME, CLICK_SEND_PASSWORD]
---
# ClickSend API
Use the `http` tool. Credentials are automatically injected.
## Base URL
`https://rest.clicksend.com/v3`
## Actions
**Send SMS:**
```
http(method="POST", url="https://rest.clicksend.com/v3/sms/send", body={"messages": [{"to": "+1234567890","body": "Hello!","source": "sdk"}]})
```
**Get SMS history:**
```
http(method="GET", url="https://rest.clicksend.com/v3/sms/history?page=1&limit=10")
```
**Send email:**
```
http(method="POST", url="https://rest.clicksend.com/v3/email/send", body={"to": [{"email": "recipient@example.com","name": "John"}],"from": {"email": "sender@example.com"},"subject": "Hello","body": "<p>Content</p>"})
```
## Notes
- Uses Basic auth with username and API key.
- SMS `to` must include country code (e.g., `+1234567890`).
- Supports SMS, MMS, email, voice, fax, and postal mail.
+83
View File
@@ -0,0 +1,83 @@
---
name: clickup
version: "1.0.0"
description: ClickUp API v2 — tasks, spaces, lists, comments, time tracking
activation:
keywords:
- "clickup"
- "clickup task"
- "clickup space"
exclude_keywords:
- "jira"
- "asana"
patterns:
- "(?i)clickup.*(task|space|list|folder)"
- "(?i)(create|list|update).*clickup"
tags:
- "project-management"
- "task-management"
max_context_tokens: 1500
metadata:
openclaw:
requires:
env: [CLICKUP_API_TOKEN]
---
# ClickUp API v2
Use the `http` tool. Credentials are automatically injected for `api.clickup.com`.
## Base URL
`https://api.clickup.com/api/v2`
## Actions
**List workspaces (teams):**
```
http(method="GET", url="https://api.clickup.com/api/v2/team")
```
**List spaces:**
```
http(method="GET", url="https://api.clickup.com/api/v2/team/<team_id>/space")
```
**List folders in space:**
```
http(method="GET", url="https://api.clickup.com/api/v2/space/<space_id>/folder")
```
**List tasks in a list:**
```
http(method="GET", url="https://api.clickup.com/api/v2/list/<list_id>/task?page=0&subtasks=true&include_closed=false")
```
**Get task:**
```
http(method="GET", url="https://api.clickup.com/api/v2/task/<task_id>")
```
**Create task:**
```
http(method="POST", url="https://api.clickup.com/api/v2/list/<list_id>/task", body={"name": "Task name", "description": "Details", "status": "to do", "priority": 2, "due_date": 1775000000000, "assignees": [123456]})
```
**Update task:**
```
http(method="PUT", url="https://api.clickup.com/api/v2/task/<task_id>", body={"status": "in progress", "priority": 1})
```
**Add comment:**
```
http(method="POST", url="https://api.clickup.com/api/v2/task/<task_id>/comment", body={"comment_text": "My comment"})
```
## Notes
- Priority: 1=Urgent, 2=High, 3=Normal, 4=Low.
- Due dates are Unix timestamps in **milliseconds**.
- Status values are lowercase strings matching your workspace statuses.
- Task IDs are alphanumeric strings like `"abc123"`.
- Hierarchy: Team → Space → Folder → List → Task.
- Pagination: `page` param (0-indexed). Returns empty array when no more.

Some files were not shown because too many files have changed in this diff Show More