Repository navigation
Releases: tuya/agentic-kit
Release list
Release v0.5.0
Added
-
docs-site — English edition of the full documentation site, published at
/en/with Simplified Chinese retained at/.- All 29 docs are mirrored under
docs-site/i18n/en/, with a locale selector in both the landing-page navbar and the custom docs topbar, localized navbar/footer/sidebar catalogs, and English SVG schematics undercurrent/images/. - Heading anchors use explicit IDs shared across locales, and
npm run check:i18ngates path, image, and anchor parity as part ofnpm run build.
- All 29 docs are mirrored under
-
iot-client —
iot_client_config_t.skip_version_reportskips SDK-meta and firmware-version reports duringiot_client_init()(#37).- Default
falsereports both; settruewhen the cloud already has the current version.
- Default
-
tuya-ble — asynchronous nearby-WiFi lists over big-data, fixed CFG-status query replies, and an ESP-IDF scan provider (#35).
- Configure
wifi_scan_request; complete scans with their original token viatuya_ble_bigdata_wifi_list_complete()on the BLE owner context. - A scan provider enables WiFi-list/status capability discovery, not a complete PSK3.0 activation exchange.
- Configure
-
docs-site — new bilingual guide "DP (Data Point): Definition, Creation, and Usage" under User Guides → Cloud Configuration (#41).
- Covers the official three-level DP definition, the four elements (DPID/DPCode/type/constraints), the six data types, rw/ro/wr transfer modes,
dpsand cloud command-set message formats, cloud rate limits, platform-side DP creation, and DP usage in agentic-kit (schema intake, type mapping, coreiot_dp_*APIs, a typical code flow), with official and on-site references.
- Covers the official three-level DP definition, the four elements (DPID/DPCode/type/constraints), the six data types, rw/ro/wr transfer modes,
Changed
-
SDK-wide — every build-time knob is renamed with an
AGENTIC_KIT_prefix and its default moves to its owning subsystem's config file; override per product with oneagentic_kit_config.hon the include path,-D<NAME>=<value>, orAGENTIC_KIT_USER_CONFIG(#38).- BREAKING
TAI_FRAG_BUF_SIZE→AGENTIC_KIT_TAI_FRAG_BUF_SIZE,RESPONSE_BUFFER_SIZE→AGENTIC_KIT_RESPONSE_BUFFER_SIZE, and likewise the rest — old→new table indocs-site/docs/guides/compile-time-knobs.md. - Knob defaults live at: the override pickup in
common/log.h; PAL task sizing inpal/pal_config_defaults.h; per-module knobs in each module's include/ (iot_client_config_defaults.h,tai_config_defaults.h,tuya_ble_config_defaults.h). Non-knob module data (version strings, endpoints, log binding) lives in each module's src-side internal header (src/iot_internal.hrestores and renames the formeriot_config_defaults.h). - Removed the dead
IOT_DO_NOT_USE_CUSTOM_CONFIGflag from the ESP-IDF component.
- BREAKING
-
SDK-wide — one compile-time log ceiling
AGENTIC_KIT_LOG_LEVEL(0–4, default 4) gates every SDK log macro via the newlog_tag_*facade incommon/log.h; module code no longer callslog_emit()directly (#40).- BREAKING
-DTAI_LOG_LEVEL=N(rtc-tcp-client only) and the dead-DLOG_LEVELboth move to-DAGENTIC_KIT_LOG_LEVEL=N, which now silences the whole SDK at compile time. Lowering only rtc-tcp-client afterwards is the new-DAGENTIC_KIT_TAI_LOG_LEVEL=N(see below). - Optional per-module log ceilings were added back on top:
AGENTIC_KIT_IOT_LOG_LEVEL,AGENTIC_KIT_TAI_LOG_LEVELandAGENTIC_KIT_TUYA_BLE_LOG_LEVELall default to the SDK-wide ceiling and can only lower their one module below it (effective ceiling = the smaller of the two; values above the global ceiling are clamped to it once in each module's config file, so block-level gates see the effective ceiling too). They gate each module's vocabulary where it is defined, ride the same override pickup, and keep the compile-time-only property — below 3, the rtc-tcp-client packet-log formatter vanishes with its calls. tuya-ble gains its first knob and itstuya_ble_config_defaults.hthis way. - BREAKING the runtime log layer is removed:
log_set_level(),log_get_level()and thetai_set_log_level()/tai_get_log_level()wrappers are gone, andlog_emit()no longer filters — the ceiling is the only level gate, so what compiles in is what emits. Migrate runtimelog_set_level(N)calls to a lower ceiling at build time, or to level filtering inside yourAGENTIC_KIT_LOGtarget. - BREAKING the runtime handler is removed too:
log_set_handler(),log_fn_tand the publiclog_default_handler()are gone — the destination is a compile-time fact, with no mutable log state left in the SDK. Make the old handler function theAGENTIC_KIT_LOGtarget (it now receives the level and the bare tag directly), and use the newlog_emit_valist()— the facade'sva_listentry, theesp_log_writevrole — to reuse the default stderr output from a function sink. The SDK's own tests migrated the same way: each test build remaps the dispatch into one sink whose capture/quiet/count are modes, not handler swaps. - The dispatch itself is customer-remappable at compile time: defining
AGENTIC_KIT_LOG(level, tag, fmt, ...)routes every SDK log line into your own macro — straight intoESP_LOGx-style systems with nova_listround trip (the tag arrives as its own token). TheAGENTIC_KIT_LOG_LEVELceiling still applies, and since there is no runtime handler anymore, the remap is the one and only destination switch. The ESP-IDF pair-by-ble example now uses exactly this:kit_opts/agentic_kit_config.hmaps the facade ontoESP_LOG_LEVEL_LOCAL, replacing its old runtime callback (each line also keeps its real module tag instead of one shared"tuya_ble"tag). - rtc-tcp-client packet logging selects its INFO/DEBUG sink at compile time — sampling builds have exactly one level; flood mode needs
AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N=0plus a DEBUG ceiling and vanishes otherwise — and its JSON formatter compiles out below INFO. A compile-matrix test builds 19 level × sampling combinations across both the SDK-wide and the rtc-tcp-client-only ceiling, including module-above-global clamp probes.
- BREAKING
-
tuya-ble — the SDK-internal log-facade binding, scan-token rotation and pending-credential
delivery are each defined once intuya_ble_internal.hinstead of being repeated per module. -
tuya-ble — bounded per-state Trsmitr reassembly, queued TX with backpressure, and configurable radio capability (#35).
- Ports drive
tuya_ble_prov_set_gatt_payload(),tuya_ble_prov_tx_ready()andtuya_ble_prov_tick(); calltuya_ble_prov_close()on disconnect/reset. - Recompile consumers for the public state/config layout changes;
comm_ability = 0selects 2.4 GHz.
- Ports drive
-
rtc-client — upgrade libstm(#33).
-
rtc-client — frames under
TAI_FRAME_COALESCE_LIMIT(default 512 B) now coalesce into a
single transport write: Ping, control packets and small audio chunks drop from 2–3 TLS
records to 1(#32).
Fixed
- tuya-ble — accept incoming Trsmitr versions >= 2 for app Pairing compatibility while keeping TX at version 4 (#39).
- Docs — the pair-by-ble callback sample no longer claims credentials are never logged; the
demo's DEBUG protocol log prints the credential JSON on purpose, for device bring-up. - Docs — the pair-by-ble stop section now also states the port's
nimble_port_deinit()abort,
instead of implying everytuya_ble_nimble_stop()failure is retryable (#35). - tuya-ble — preserve pending credentials under TX backpressure and route Trsmitr diagnostics through the log facade (#35).
- Examples — isolate BLE WiFi scan completions across cancellation, join workers before teardown, and restart STA before connecting (#35).
- tuya-ble — enforce Pairing/KEY_12 credential authorization, reject replayed Frames, and accept the app's extra CBC padding block (#35).
- tuya-ble — reset Pairing state on device-info re-query and defer credential delivery until queued acknowledgements are accepted by the port (#35).
- iot-client — US-East (
UEAZ) fell back to an ATOP host that does not resolve(#31).
IOT_UEAZ_HOSTis nowa1-ueaz.tuyaus.com.
Release v0.4.0
Added
- iot-client — device-initiated reset (
iot_client_reset), for a device that
unbinds itself instead of waiting to be removed from the app(#27).OPRT_OKdestroys the client; any other code leaves it usable so the call
can be retried.iot_reset_scope_tchooses how much the cloud clears(#28):
IOT_RESET_UNBIND_ONLYdrops the binding and keeps the device's cloud-side
data,IOT_RESET_FACTORYalso discards that data and cannot be undone.- The optional
error_codeout-param carries the cloud's rejection reason —
needed to tell a terminalGATEWAY_NOT_EXISTS(wipe credentials, re-pair)
from a retryableREMOTE_API_RUN_UNKNOW_FAILED. - It does not erase persisted credentials/DP state/schema; that stays the
app's job. Shown inpair/api-activate --release.
- iot-client —
iot_client_get_session_token_ex()reports why the cloud
refused an agent token(#29). The refusals need opposite handling — a terminal
GATEWAY_NOT_EXISTS(device removed; re-pair) versus retryable ones — and the
plain function cannot tell them apart. - iot-client —
iot_client_connect()/iot_client_disconnect()are public(#26).
A reconnect loop previously had to reach intosrc/iot_client_message.h,
which is not installed. - iot-client — generic ATOP call (
iot_atop_call), for cloud interfaces the SDK
does not wrap by name(#19).- New public header
iot_atop.h: pass anapiname, itsversionand a JSON
body; get the envelope'sresultback as a JSON string plus the cloud's
errorCode/errorMsg. Activated devices only. docs-site/docs/guides/atop-generic-call.mdlists the named wrappers and
when to promote an interface to one.
- New public header
- iot-client — cloud device-remove (protocol 11) callback(#18).
- New
iot_reset_callback_t/iot_reset_type_t; registering it makes the
SDK consume protocol-11 notices instead of passing them to
message_callback.
- New
- iot-client — OTA firmware digest verification (
iot_ota_verify_*)(#17).- Streaming verifier (
init/update/finish/abort) so a download can
be checked without buffering the image.
- Streaming verifier (
- common — coreMQTT and coreHTTP Error/Warn logs are routed into the SDK log
facade (common/core_{mqtt,http}_config.h)(#24, #25).- A refused CONNECT now names the reason (
Connection refused: bad user name or password.) instead of a bareMQTT_Connect failed: 6, and an oversized
ATOP response saysinsufficient space: responseBufferLen=.... mqtt.c's own log lines print the symbolicMQTTStatus_tname alongside the
number.docs-site/docs/reference/iot-client.mdgains status and CONNACK
tables for reading older logs.- Do not set
MQTT_DO_NOT_USE_CUSTOM_CONFIG/HTTP_DO_NOT_USE_CUSTOM_CONFIG
again — in either build path — or the reasons go silent.
- A refused CONNECT now names the reason (
- iot-client — APP-confirmed OTA notice (protocol 15) callback(#21).
- New
iot_ota_confirm_callback_ton both config structs: the cloud pushes it
once the user confirms the upgrade in the app, with the firmware channel.
Registering it makes the SDK consume protocol-15 notices.
- New
- Examples — device-unbind demo (
unbind_demo)(#18). - Examples — agent trigger demo (
tai_agent_trigger_demo)(#22). - Examples — music play demo (
tai_music_play_demo)(#12). - rtc-tcp-client — send an image and streamed audio as ONE multimodal event
(tai_send_image_audio_start()/_chunk()/_end())(#20). - rtc-tcp-client — per-connection custom parameters on
EventStartvia
tai_config_t.event_custom_param_json(#20). - rtc-tcp-client —
tai_set_event_params()changes event parameters per turn,
for devices that alternate between turn kinds(#20).
Changed
- BREAKING iot-client — MQTT auto-connect is on by default;
mqtt_auto_connectbecomesmqtt_disable_auto_connecton both config
structs(#26).- Migration:
.mqtt_auto_connect = false→.mqtt_disable_auto_connect = true;
.mqtt_auto_connect = truecan be deleted. The rename means old code fails
to compile rather than silently changing behaviour. - A failed auto-connect still returns
NULLfromiot_client_init(). Devices
that may boot without a network, or that use ATOP over HTTP with no broker,
must now opt out and drive the link themselves.
- Migration:
- BREAKING iot-client —
iot_get_qrcode_infoandiot_get_ca_certificate
write into caller-provided buffers;iot_qrcode_response_tis removed(#10). - BREAKING Examples — every POSIX example binary is named after its source
file. The rtc-client (prebuilt UDP lib) demos gain audp_prefix:
chat_demo/edu_camera_demobecomeudp_chat_demo/udp_edu_camera_demo. - Docs — the cloud-VAD turn boundary is
TAI_EVT_CHAT_BREAK, not
TAI_EVT_SERVER_VAD.- The cloud no longer sends
TAI_EVT_SERVER_VAD(type 5) in cloud-VAD mode; it
ends a user turn with an inboundTAI_EVT_CHAT_BREAK(type 4). The constant
is kept for protocol compatibility but marked legacy.
- The cloud no longer sends
- Observability — diagnostic logging on previously-silent error paths (core
modules + PAL)(#11). PAL logserrnoon socket failures;tls_read/tls_write
log the raw mbedTLS cause instead of collapsing toTLS_ERR_NET. - Examples — the rtc-tcp-client POSIX demos share their parsing helpers
(demo_json.h,demo_text.h)(#14).
Fixed
- iot-client — a cloud-rejected ATOP call reported success(#19). A
success: false
envelope loggederrorCode/errorMsg, dropped them and returnedOPRT_OK;
it now returnsOPRT_ATOP_BUSINESS_ERRORuniformly, with both strings on the
response. Callers needing per-code policy branch onerror_code. - iot-client — a peer that closed a non-TLS MQTT connection went unnoticed until
the 60 s keepalive expired(#26):transport_recv()passed a 0 (EOF perpal.h)
straight to coreMQTT, which reads it as "no data yet". - iot-client — tearing down an already-dead link no longer pushes a DISCONNECT
into a socket that cannot carry it(#26). OnlyMQTTRecvFailed/MQTTSendFailed/
MQTTKeepAliveTimeoutcount as dead; a completed exchange clears the flag. - iot-client —
iot_client_deinit()wipes the client before freeing it(#27).devid,
secret_keyandlocal_keyare plaintext iniot_client_t, and an embedded
allocator hands the block to whatever mallocs next. - iot-client — US-East (
UEAZ) and West-Europe (WEAZ) never resolved an MQTT
broker(#15).iot_region_to_string()sent the enum name where the service
expects the two-letter activation-token prefix. - iot-client — an ATOP response larger than the buffer reported a generic
communication error(#23).REQUEST_HEADER_BUFFER_SIZEand
RESPONSE_BUFFER_SIZEare now#ifndef-guarded so an application can size
them with-D, andHTTPInsufficientMemorylogs what was needed versus what
the buffer holds. A product whose schema came back at contentLength 6558
against the 4096 default looked like a network fault. - iot-client — SG region no longer falls back to the China ATOP host(#9).
- iot-client — US region renamed to
AZ(#7). The DNS region string and token
prefix for US West (Oregon) isAZ, notUS. - common/tls — a TLS 1.3
NewSessionTickettore the session down(#20).
MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKETis a non-fatal "read again"
signal thattls_read()treated as an error. - Examples —
unbind-demo --resetno longer requires a working MQTT connection(#27).
Reset travels over ATOP HTTPS, and gating it on CONNECT disabled the flag in
the one case it exists for: a device the cloud has already unbound. - Examples —
audio_chat_democould not parse its session token on hosts where
/opt/homebrew/includeholds mbedtls 4.x headers: that directory lands first
on this one target's include path (viafind_path(opus)), shadowing the
vendored 3.6.6 headers. - Examples —
audio_chat_demosends its one-shot file audio under the device-VAD
attributes it actually implements (asr.enableVad). - Examples — the tool-less rtc-tcp-client demos answer MCP requests correctly:
each reply now echoes the request id instead of a hardcoded"id":1(#14). - Examples — text streams that cannot be reassembled are reported, not dropped
silently(#14). - Examples — NLG prose is unescaped before printing(#14).
- Examples — a value too long for a display-only field truncates instead of
being emptied(#14). - Examples — out-of-bounds read on received text:
tai_text_msg_t.textis a
borrowed, non-NUL-terminated slice(#14). - Examples — stack overflow from over-long
argvcredentials(#14).
Release v0.3.0
Added
- IoT Client — OTA firmware upgrade support(#3).
iot_ota_report_version— reports the device's current firmware version
(auto-called duringiot_client_initfromiot_client_config_t.sw_ver, so
the cloud can evaluate upgrades against the running version).iot_ota_check_upgrade— queries the cloud for a pending upgrade and
returns version, download URL (cdnUrlpreferred,httpsUrlfallback),
file size, and MD5/HMAC hashes. Takes nosw_verargument — the cloud
compares against the version already reported at init.iot_ota_report_status— drives the upgrade lifecycle
(UPGRADING → FINI / EXEC / ABORT).
- RTC TCP Client (
tuya_ai) — received images delivered viaon_imagecallback(#6).- New
tai_image_msg_t+on_imagecallback (message-struct API); format/width/height parsed from image-params on START/ONE_SHOT. TAI_PKT_IMAGEnow handled intai_proto_dispatch(was dropped as unknown) — strips the 8-byte media header and emits each chunk for the caller to reassemble START..END / ONE_SHOT.
- New
Changed
- PAL / common — TCP connect timeout(#1).
tcp_connecttakes atimeout_ms, bounded byselect()(0 = single non-blocking attempt).- Threaded through all call sites (ai-tcp, HTTP, MQTT's new
MQTT_CONNECT_TIMEOUT_MS). tls_config_t's two timeouts collapse intoconnect_timeout_ms— one deadline for TCP connect + TLS handshake (removeshandshake_timeout_ms).
- common / TLS — ESP-IDF cert-bundle decoupled via callback(#2).
- iot-client / common — memory-management pass(#4).
iot_client_tinlineshttps_url/mqtt_urlaschar[64]and the DP context as inline storage (werestrdup/ lazymalloc),_Static_assert-guarded.- Per-op allocations removed: stacked atop sign buffer + MQTT subscribe/publish topics; HTTP request-header and response share one allocation; DP report/state returns the cJSON string directly;
tai_pkt_logformats into a stack buffer. - mbedTLS global config (allocator, record sizes) left to the integrator — removed the SDK-side
MBEDTLS_USER_CONFIG_FILEwiring, ownership documented incommon/tls.h.
- RTC TCP Client (
tuya_ai) — protocol-layer redesign.- Receive callbacks are now struct-based (ABI break).
- Downstream audio-params re-read per stream.
- Scatter-gather streaming send.
- Smaller receive buffers via a smaller max fragment.
Fixed
- RTC TCP Client (
tuya_ai) — protocol-layer redesign.tai_disconnectholds the send lock across the transport close (fixes a use-after-free).- Unknown downstream Packet/Event types are tolerated (logged + skipped); malformed packets stay fail-fast.
- Oversized inbound frame is fail-fast (
TAI_PROTO_ERR_OVERSIZED) instead of stalling to the liveness timeout. on_disconnectfires at most once per Connection, for terminal disconnects only.on_disconnectis no longer fired on the connecting thread.- Unsigned ClientHello framed into a
TAI_TX_CTRL_BUF_SIZEbuffer (was a fixed 256 B). - A send failure after committing bytes no longer rolls back the sequence number.
gen_idfails cleanly when the RNG errors, instead of emitting an uninitialized id.- Graceful peer TLS close (
close_notify) surfaced as a comms error, detected immediately. - Shared process-wide RNG (
rng_bytes) serialized by a pal mutex (fixes a CTR-DRBG data race). iot_initnow fails if RNG seeding fails.tai_connectcompletes on the server'sAuthenticateResponse(type 3), not only aSessionNewack.tai_disconnectreturns promptly on an idle link (worker poll capped byTAI_WORKER_POLL_CAP_MS).
- PAL (FreeRTOS) — allocator mismatch: the worker-thread struct is freed with
pal_free, notvPortFree. - atop —
atop_activate_requeststack-allocates its response like the other ATOP calls, fixing an OOM leak of the POST-body buffer on the response-malloc-failure path. - common / TLS — hardening follow-ups.
- Restored human-readable X.509 verification diagnostics (
mbedtls_x509_crt_verify_info). rng_bytes()/pv23_encrypt()reject a NULLpal; redundantmemset/strlendropped intls_connect.
- Restored human-readable X.509 verification diagnostics (
Release v0.2.0
Added
- IoT Client — Data Point (DP) management layer (
modules/iot-client/include/iot_dp.h).
Sits on top of the existing transport: uplink reusesiot_client_publish(), downlink is
intercepted on theiot_client_process()receive path — no new crypto path, threads, or timers.- Schema registry with typed validation (type / range / maxlen / enum) and a per-DP dirty
bit; an empty or invalid schema falls back to loose pass-through mode. - Uplink reporting:
iot_dp_report(single) andiot_dp_report_all_dirty/
iot_dp_report_all(batched), guarded by the MQTT packet budget
(OPRT_DP_PAYLOAD_TOO_LARGE). - Downlink protocol-5 DP-set dispatch with a per-DP change callback.
- Persistence mechanism (the application decides when/where):
iot_dp_dump_json,
iot_dp_validate_json(strict) andiot_dp_restore_json(lenient); RAW DPs are reported
uplink but never persisted. - Non-destructive schema upgrade via
iot_dp_schema_check_update()(existing values kept,
new DPs defaulted). - DP access mode (
ro/rw/wr) from the product schema is treated as a cloud-side hint
and is not enforced by the device SDK — the device may set/report any DP.
- Schema registry with typed validation (type / range / maxlen / enum) and a per-DP dirty
- POSIX
dp_management_demoexample demonstrating the DP layer end to end (connect,
restore, report, downlink, persist). - MCP
mcp_exampleexample demonstrating MCP tool integration with the agentic-kit
framework.
Release v0.1.0
Implement the basic functionality of Agentic-kit.