Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: DuckyClaw Check Build
name: TuyaOpenClaw Check Build
run-name: ${{ github.actor }} triggered build check
on:
push:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: DuckyClaw Release
name: TuyaOpenClaw Release
run-name: Release ${{ github.ref_name }}

on:
Expand All @@ -10,11 +10,11 @@ permissions:
contents: write

env:
GITEE_REPO: tuya-open/DuckyClaw
GITEE_REPO: tuya-open/TuyaOpenClaw

jobs:
sync-gitee:
if: github.repository == 'tuya/DuckyClaw'
if: github.repository == 'tuya/TuyaOpenClaw'
runs-on: ubuntu-latest
steps:
- name: Checkout GitHub code
Expand Down Expand Up @@ -126,7 +126,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

gitee-release:
if: github.repository == 'tuya/DuckyClaw'
if: github.repository == 'tuya/TuyaOpenClaw'
runs-on: ubuntu-latest
needs:
- sync-gitee
Expand Down Expand Up @@ -174,7 +174,7 @@ jobs:
"tag_name": os.environ["TAG_NAME"],
"target_commitish": os.environ["TARGET_COMMITISH"],
"name": "Release " + os.environ["TAG_NAME"],
"body": "See release notes at https://github.com/tuya/DuckyClaw/releases/tag/" + os.environ["TAG_NAME"],
"body": "See release notes at https://github.com/tuya/TuyaOpenClaw/releases/tag/" + os.environ["TAG_NAME"],
"draft": False,
"prerelease": False,
}))'
Expand Down
16 changes: 8 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AGENTS.md — DuckyClaw Project Guide
# AGENTS.md — TuyaOpenClaw Project Guide

> This file provides project-level context for AI coding assistants and new contributors:
> business logic, technical architecture, directory layout, and coding guidelines.
Expand All @@ -7,7 +7,7 @@

## 1. Business Logic

DuckyClaw is a **hardware-oriented AI agent running on edge devices**. Its core value is enabling users to interact with IoT devices through natural language via IM channels (Telegram / Discord / Feishu). The on-device agent receives instructions, invokes local MCP tools to perform real actions, and replies through the same channel.
TuyaOpenClaw is a **hardware-oriented AI agent running on edge devices**. Its core value is enabling users to interact with IoT devices through natural language via IM channels (Telegram / Discord / Feishu). The on-device agent receives instructions, invokes local MCP tools to perform real actions, and replies through the same channel.

**Typical user scenarios:**

Expand Down Expand Up @@ -69,7 +69,7 @@ DuckyClaw is a **hardware-oriented AI agent running on edge devices**. Its core
1. **Inbound**: IM channel (or WS/cron/ACP) builds an `im_msg_t` → `message_bus_push_inbound()`
2. **Agent consumes**: `agent_loop_task` blocks on `message_bus_pop_inbound()`
3. **Inner loop**: `context_build_system_prompt()` assembles system prompt + history → `ai_agent_send_text()` sends to cloud
4. **AI callback**: `ducky_claw_chat.c`'s `__ai_chat_handle_event` receives stream events:
4. **AI callback**: `tuyaopen_claw_chat.c`'s `__ai_chat_handle_event` receives stream events:
- `STREAM_START/DATA/STOP`: accumulate text → record in history
- `END`: calls `agent_loop_set_last_response()` + `agent_loop_notify_turn_done()` (posts semaphore)
5. **Tool execution**: Cloud AI triggers MCP tool call → `__on_tool_executed` hook records result → sets `s_turn.tool_called = true`
Expand All @@ -90,7 +90,7 @@ DuckyClaw is a **hardware-oriented AI agent running on edge devices**. Its core

### 2.4 Synchronization

- **Agent loop ↔ AI callback**: Binary semaphore `s_turn.sem` (`agent_loop_task` waits, `ducky_claw_chat` posts)
- **Agent loop ↔ AI callback**: Binary semaphore `s_turn.sem` (`agent_loop_task` waits, `tuyaopen_claw_chat` posts)
- **Shared history**: `s_history_mutex` protects `s_history_json` (cJSON array, sliding window ≤ 10 entries)
- **Tool state**: `s_turn.lock` protects `tool_called` / `tool_result`
- **Message bus**: `tal_queue` is inherently thread-safe
Expand All @@ -100,7 +100,7 @@ DuckyClaw is a **hardware-oriented AI agent running on edge devices**. Its core
## 3. Directory Layout

```
DuckyClaw/
TuyaOpenClaw/ (git repo: tuya/TuyaOpenClaw, a.k.a. Ducky)
├── agent/ # Core agent loop and context builder
│ ├── agent_loop.c/h # Outer + inner tool-iteration loop (semaphore sync)
│ └── context_builder.c/h # System prompt assembly (rules, memory, skills, personality)
Expand Down Expand Up @@ -141,7 +141,7 @@ DuckyClaw/
├── src/ # Application glue layer
│ ├── tuya_app_main.c # Entry point user_main(), initialization orchestration
│ ├── ducky_claw_chat.c # AI stream event handling, semaphore bridge to agent_loop
│ ├── tuyaopen_claw_chat.c # AI stream event handling, semaphore bridge to agent_loop
│ ├── app_im.c # IM init, outbound dispatch, channel switching
│ ├── cli_cmd.c # Extended CLI commands
│ └── reset_netcfg.c # Network config reset logic
Expand All @@ -150,7 +150,7 @@ DuckyClaw/
│ ├── tuya_app_config.h # Product ID, channel tokens, gateway config defaults
│ ├── tuya_app_config_secrets.h(.example) # Sensitive credentials (gitignored)
│ ├── app_im.h # IM application interface
│ └── ducky_claw_chat.h # Chat module interface
│ └── tuyaopen_claw_chat.h # Chat module interface
├── ai_components/ # TuyaOpen AI component adapters (sub-CMake)
├── config/ # Board-level Kconfig snapshots
Expand All @@ -176,7 +176,7 @@ user_main()
├── tuya_iot_init() // Tuya IoT client
├── netmgr_init() // Network manager
├── board_register_hardware()
├── ducky_claw_chat_init() // Register AI stream event callback
├── tuyaopen_claw_chat_init() // Register AI stream event callback
├── app_im_init() // Subscribe to MQTT connected event (deferred IM init)
├── ws_server_start() // Start WebSocket server
├── tool_registry_init() // Subscribe to MQTT connected → one-shot MCP tool chain init
Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

DuckyClaw is a hardware-oriented AI agent built on the TuyaOpen C SDK. It runs a Claw-style agent loop on edge devices (Tuya T5AI, ESP32, Raspberry Pi, Linux) that communicates with users via IM channels (Telegram, Discord, Feishu) and executes MCP-style tools on the device.
TuyaOpenClaw is a hardware-oriented AI agent built on the TuyaOpen C SDK. It runs a Claw-style agent loop on edge devices (Tuya T5AI, ESP32, Raspberry Pi, Linux) that communicates with users via IM channels (Telegram, Discord, Feishu) and executes MCP-style tools on the device.

## Build Commands

DuckyClaw builds as a TuyaOpen application. From the repo root:
TuyaOpenClaw builds as a TuyaOpen application. From the repo root:

```bash
# Initialize TuyaOpen environment (creates .venv, exports OPEN_SDK_ROOT)
Expand All @@ -20,7 +20,7 @@ cp config/RaspberryPi.config app_default.config # Raspberry Pi
cp config/TUYA_T5AI_BOARD_LCD_3.5_CAMERA.config app_default.config # Tuya T5AI
cp config/ESP32S3_BREAD_COMPACT_WIFI.config app_default.config # ESP32-S3

# Build (from TuyaOpen directory, pointing to DuckyClaw as the app)
# Build (from TuyaOpen directory, pointing to TuyaOpenClaw as the app)
cd TuyaOpen
python3 tos.py build

Expand Down Expand Up @@ -48,7 +48,7 @@ The secrets file is gitignored. Defaults live in `include/tuya_app_config.h`.
The core is a synchronous outer+inner loop in `agent_loop.c`:
- **Outer loop**: blocks on `message_bus_pop_inbound()` waiting for user messages from any IM channel
- **Inner loop** (up to `TOOL_LOOP_MAX=10` iterations): sends prompt to cloud AI via `ai_agent_send_text()`, blocks on a semaphore until the AI turn completes, checks if a tool was called, and either loops (feeding tool result back) or forwards the final response to IM
- Synchronization: `ducky_claw_chat.c` receives streaming AI events and calls `agent_loop_notify_turn_done()` on `AI_USER_EVT_END`
- Synchronization: `tuyaopen_claw_chat.c` receives streaming AI events and calls `agent_loop_notify_turn_done()` on `AI_USER_EVT_END`
- Tool results are captured by `__on_tool_executed` hook (registered via `ai_mcp_server_set_tool_exec_hook`)

### Context Builder (`agent/context_builder.c`)
Expand Down
8 changes: 4 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,17 @@ if(_APP_NEW_LIBS)
endif()

########################################
# DuckyClaw ai_components overlay
# TuyaOpenClaw ai_components overlay
########################################
# Surgically replace individual translation units inside the upstream
# TuyaOpen ai_components library targets (ai_mcp, ai_agent) with
# DuckyClaw-side patches kept under overlay/. The SDK submodule stays
# TuyaOpenClaw-side patches kept under overlay/. The SDK submodule stays
# pristine; only this app's CMake graph diverges. TuyaOpen processes
# src/ai_components/ before this file is `include`d, so the upstream targets
# already exist when we reach here.
#
# Note: ai_main and ai_audio used to be overlayed too, but those diffs have
# been folded back into upstream / proven trivial — DuckyClaw now consumes
# been folded back into upstream / proven trivial — TuyaOpenClaw now consumes
# them straight from TuyaOpen/src/ai_components/ai_{main,audio}/.
set(_DC_OVERLAY ${APP_PATH}/overlay/ai_components)

Expand Down Expand Up @@ -135,7 +135,7 @@ endfunction()
# the lib's own CMakeLists BEFORE our overlay ran, so EXAMPLE_LIB also needs
# the overlay path explicitly — otherwise app sources that compile into
# EXAMPLE_LIB (agent_loop.c, ducky_claw_chat.c, app_im.c, …) keep seeing the
# upstream headers without DuckyClaw's added declarations.
# upstream headers without TuyaOpenClaw's added declarations.
foreach(_lib_hdir ai_mcp ai_agent)
if(TARGET ${_lib_hdir})
target_include_directories(${_lib_hdir} BEFORE PUBLIC ${_DC_OVERLAY}/${_lib_hdir}/include)
Expand Down
12 changes: 6 additions & 6 deletions Kconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
menu "configure app (DuckyClaw) "
menu "configure app (TuyaOpenClaw) "

config SYS_BUS_OUTBOUND_STACK_SIZE
int "SYS bus outbound thread stack size (bytes)"
Expand All @@ -12,20 +12,20 @@ menu "configure app (DuckyClaw) "
help
Stack size in bytes for the IM bridge thread (default 4*1024).

config DUCKYCLAW_USE_AI_COMPONENTS
config TUYAOPENCLAW_USE_AI_COMPONENTS
bool
default y
select ENABLE_AI_COMPONENTS

# DuckyClaw moves large app-level buffers (acp_client / ws_server / IM dedup rings)
# TuyaOpenClaw moves large app-level buffers (acp_client / ws_server / IM dedup rings)
# from static .bss into PSRAM heap via claw_malloc / im_calloc, which only does
# anything when ENABLE_EXT_RAM is on at the SDK level. Some board Kconfigs
# (e.g. ESP32S3_BREAD_COMPACT_WIFI in upstream TuyaOpen) don't `select
# ENABLE_EXT_RAM` on their own — opt into it from the app side so DuckyClaw
# ENABLE_EXT_RAM` on their own — opt into it from the app side so TuyaOpenClaw
# doesn't have to patch the submodule. Boards that already select it
# (DNESP32S3 family, WAVESHARE_ESP32S3, T5AI variants with PSRAM) are unaffected.
config DUCKYCLAW_USE_EXT_RAM
bool "DuckyClaw: enable external PSRAM heap"
config TUYAOPENCLAW_USE_EXT_RAM
bool "TuyaOpenClaw: enable external PSRAM heap"
default y
select ENABLE_EXT_RAM
help
Expand Down
Loading
Loading