agent-desktop-ffi
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseagent-desktop-ffi
agent-desktop-ffi
Direct C-ABI access to PlatformAdapter operations. Build the cdylib with the
profile:
release-ffish
cargo build --profile release-ffi -p agent-desktop-ffiThe output is ( on Linux,
on Windows) plus a committed C header at
. The default profile uses
: a Rust panic inside an function would
the host. keeps , which the panic traps need.
target/release-ffi/libagent_desktop_ffi.dylib.soagent_desktop_ffi.dllcrates/ffi/include/agent_desktop.hreleasepanic = "abort"extern "C"SIGABRTrelease-ffipanic = "unwind"Four reference topics, loaded as needed:
- ownership.md: who allocates and who frees every pointer the FFI hands back.
- error-handling.md: errno-style last-error contract, error codes, enum validation, panic boundary.
- threading.md: host-thread contract, mutation ordering across threads and processes, adapter-bound native handles, AXIsProcessTrusted inheritance.
- build-and-link.md: prebuilt archives, ABI handshake, struct size validation, C and Python examples.
直接通过 C-ABI 访问 PlatformAdapter 操作。使用
profile 构建 cdylib:
release-ffish
cargo build --profile release-ffi -p agent-desktop-ffi输出为 (Linux 上为 ,
Windows 上为 ),并在
提供一个已提交的 C header。默认
profile 使用 : 函数内部发生 Rust panic 会让宿主进程触发
。 保留 ,这是 panic 捕获机制所需的配置。
target/release-ffi/libagent_desktop_ffi.dylib.soagent_desktop_ffi.dllcrates/ffi/include/agent_desktop.hreleasepanic = "abort"extern "C"SIGABRTrelease-ffipanic = "unwind"四个 reference topics,可按需加载:
- ownership.md:FFI 返回的每个指针由谁分配、由谁释放。
- error-handling.md:errno 风格的 last-error 约定、错误码、 enum 校验和 panic 边界。
- threading.md:宿主线程约定、跨线程和进程的修改顺序、 绑定到 adapter 的 native handles,以及 AXIsProcessTrusted 继承机制。
- build-and-link.md:预构建归档、ABI 握手、 结构体大小校验,以及 C 和 Python 示例。
Observe-act workflow (canonical path)
Observe-act workflow(规范路径)
ad_init(AD_ABI_VERSION_MAJOR) // verify header ↔ dylib match
adapter = ad_adapter_create_with_session("s1") // or ad_adapter_create()
rc = ad_snapshot(adapter, "Finder", 0, 10, false, false, &json_out)
// parse json_out: locate snapshot-qualified refs in data.tree
ad_free_string(json_out)
// build action:
AdAction act = {0}; act.kind = AD_ACTION_KIND_CLICK;
rc = ad_execute_by_ref(adapter, "@s8f3k2p9:e5", &act, 0, &result_out)
ad_free_string(result_out)
ad_adapter_destroy(adapter)ad_snapshot{version, ok, command, data}data.treedata.completefalsead_snapshotapp--root@eNAD_RESULT_ERR_INVALID_ARGSad_init(AD_ABI_VERSION_MAJOR) // verify header ↔ dylib match
adapter = ad_adapter_create_with_session("s1") // or ad_adapter_create()
rc = ad_snapshot(adapter, "Finder", 0, 10, false, false, &json_out)
// parse json_out: locate snapshot-qualified refs in data.tree
ad_free_string(json_out)
// build action:
AdAction act = {0}; act.kind = AD_ACTION_KIND_CLICK;
rc = ad_execute_by_ref(adapter, "@s8f3k2p9:e5", &act, 0, &result_out)
ad_free_string(result_out)
ad_adapter_destroy(adapter)ad_snapshot{version, ok, command, data}data.treedata.completefalsead_snapshotapp--root@eNAD_RESULT_ERR_INVALID_ARGSCore constraints
核心约束
-
ABI handshake. Callonce after loading the library. A mismatch returns
ad_init(AD_ABI_VERSION_MAJOR): abort.AD_RESULT_ERR_INVALID_ARGSreturns the library's major for display. The major increments on a removed symbol or a layout change; newad_abi_version()symbols and error codes are additive. Pin the exactad_*version you link against.libagent_desktop_ffi -
Session adapters.scopes refmap persistence to a session namespace, like CLI
ad_adapter_create_with_session("session-id"). IDs are 1 to 64 ASCII alphanumeric,--session <id>, or-characters. A null ID behaves like_. An invalid ID returns null (readad_adapter_create()).ad_last_error_* -
Ref actions.is the primary action entrypoint. It loads the RefStore, resolves the ref strictly (
ad_execute_by_refwhen the snapshot has no such ref or the element changed,STALE_REFon several candidates), runs the live actionability preflight (visible, stable, enabled, supported action, policy, editable), then dispatches. A disabled or unsupported target fails before any platform call; onAMBIGUOUS_TARGETthe check report is JSON inAD_RESULT_ERR_ACTION_FAILED. It auto-waits 5000 ms before the preflight;ad_last_error_details()takesad_execute_by_ref_timeout(timeout_ms= 5000 ms default,-1= single-shot).0defaults toPressKey; every other action, includingfocus_fallback, defaults toTypeText. Passheadless(1) to allow focus without cursor movement, orAD_POLICY_KIND_FOCUS_FALLBACK(2) to allow physical cursor and keyboard delivery.AD_POLICY_KIND_HEADEDruns the same preflight from anad_execute_ref_action_exact_with_policy.AdExactRefEntry -
Generation-safe direct APIs. Every direct targeting function takes process-generation evidence:,
AdExactRefEntry,AdExactWindowInfo,ad_list_windows_exact, and the otherad_launch_app_exactsymbols.*_exact,AdRefEntry, andAdWindowInfoexist only as fields embedded in the exact structs.AdSurfaceInfokeeps the surfacead_list_surfaces_exact. Fromid2,AD_EXACT_SURFACE_INFO_VERSIONnames the surface kinds its probe could not read (for exampleAdExactSurfaceInfo.unclassified), or is null. A missingsheet,menuorsheetnext to a listed kind is unknown, not absent. Only the Windows adapter fills it; macOS and Linux return null.menu -
Native handles.and
ad_resolve_element_exactreturn anad_find_exact.AdNativeHandle(headless, no preflight) andad_execute_actionact on it directly, bypassing the ref-action pipeline. Release every handle withad_execute_action_with_policyon the same adapter and thread, beforead_free_handle(adapter, &handle), on every platform. It zeroesad_adapter_destroy, so a second call is a no-op.handle.ptr -
Action results.mirrors the CLI
AdActionResult.stepsarray for activation-chain actions. EachstepshasAdActionStepandlabelstrings,outcome(mechanism= 1 orAD_STEP_MECHANISM_SEMANTIC_API= 2, valid only whenAD_STEP_MECHANISM_PHYSICAL_SYNTHETIC), andhas_mechanism(valid only whenverified).has_verifiedcarries the same delivery/retry pair asAdActionResult.disposition. Release the result withad_last_error_delivery_semantics.ad_free_action_result(&out) -
Display discovery. Callbefore using
ad_list_displays. List order is the screenshot index order. EachAdScreenshotTarget.screen_indexcarries a stable display ID, bounds, primary flag, and scale. Release the list withAdDisplayInfo.ad_display_list_free -
Errors. A failing call sets thread-local last-error state; read it with theaccessors before the next failing call.
ad_last_error_*returns structured JSON (the actionability report onad_last_error_details(), candidate summaries onACTION_FAILED) that can contain element names, values, and window titles from the user's screen: keep it out of shared logs.AMBIGUOUS_TARGETreports whether the failed action was delivered and whether a retry is safe. Invalid enum discriminants returnad_last_error_delivery_semantics. See error-handling.md.AD_RESULT_ERR_INVALID_ARGS -
Threading. Entrypoints may be called from any host thread. Native handles stay on the adapter and thread that created them; pass qualified refs across threads. Desktop mutations take the same cross-process interaction lease as the CLI; reads take no lease and carry finite deadlines. See threading.md for the read/read, read/mutation, and mutation/mutation matrix.
-
Tracing. File tracing (same JSONL contract as CLI; segments carry
--trace,event,ts_ms, and redacted fields) is active only when the session has a manifest withseq, created bytrace: on(or an equivalent manifest on disk) before the adapter is created.session startalone writes nothing. Each OS process appends to one segment,ad_adapter_create_with_session, and a long-lived host reuses it.~/.agent-desktop/sessions/<id>/trace/<pid>-<procTs>.jsonl(JSON envelope) andad_trace_show(self-contained HTML file) read the adapter session's merged timeline;ad_trace_exportlimit= CLI default.-1 -
Log callback.registers one process-wide callback for diagnostics regardless of session manifest. It receives, as JSON, the
ad_set_log_callback(cb)events emitted inside latertracingcalls on the same thread; the FFI never installs or replaces the host process's global tracing subscriber.ad_*receives ancblevel (1=ERROR ... 5=TRACE) and aint32_tvalid only for the duration of the call. Passconst char *msgto unregister. Sensitive field values (password, token, text, ...) are replaced withNULL. The callback must not unwind across the C ABI: catch C++ exceptions and Rust panics inside it, because a violation may abort the host. It may fire from several host threads at once and briefly after a{"redacted":true}unregister: keep the callback and any data it captures valid for the process lifetime.NULL -
Wait.runs the CLI
ad_wait(adapter, args, &out)command. Setwait(mode.pause, ms),AdOptionalU64,mode.element(waits for a window with that title),mode.window, or one ofmode.text,mode.surfaces.menu,.menu_closed; add the.notificationgroup (predicate,predicate,value,action) andcount/scope.timeout_ms. The CLI event wait (scope.app,--event) is not exposed. Zero-initialize--window-idand validate the struct size againstAdWaitArgs/AD_WAIT_ARGS_SIZEbefore calling. The output is aad_wait_args_size()envelope freed with{version, ok, command, data}.ad_free_stringblocks the calling thread up toad_waitms. A concurrentscope.timeout_msdoes not invalidate an in-flight wait.ad_adapter_destroy -
Text input privacy. On macOS, focus-fallback or headed text insertion may briefly use the clipboard for non-ASCII text. For sensitive text, preferwith
AD_ACTION_KIND_SET_VALUEwhen the target supports settable values.AD_POLICY_KIND_HEADLESS -
vs
ad_get_tree_exact.ad_snapshotreturns a raw flat breadth-first tree: no refs, no refmap persistence, no JSON envelope (free it withad_get_tree_exact). Use it for custom traversal or UI inspection. For observe-act agents that drive actions viaad_free_tree, start withad_execute_by_ref.ad_snapshot
-
ABI 握手。 加载库后,调用一次。 如果版本不匹配,会返回
ad_init(AD_ABI_VERSION_MAJOR),调用方应中止。AD_RESULT_ERR_INVALID_ARGS返回库的主要版本,供显示使用。当 symbol 被移除或 布局发生变化时,主要版本会递增;新的ad_abi_version()symbol 和错误码只做增量添加。 请固定所链接的准确ad_*版本。libagent_desktop_ffi -
Session adapters。将 refmap 持久化限定在一个 session namespace 中,效果类似 CLI
ad_adapter_create_with_session("session-id")。 ID 长度为 1 到 64 个字符,只能包含 ASCII 字母数字字符、--session <id>或-。 传入 null ID 时,其行为与_相同。 无效 ID 会返回 null(读取ad_adapter_create())。ad_last_error_* -
Ref actions。是主要的 action entrypoint。 它加载 RefStore,严格解析 ref:snapshot 中不存在该 ref 或 element 已发生变化时 返回
ad_execute_by_ref,匹配到多个候选项时返回STALE_REF; 随后执行实时 actionability preflight(目标可见、稳定、已启用、支持该操作、 符合策略且可编辑),然后进行分发。disabled 或 unsupported target 会在任何 platform call 之前失败;发生AMBIGUOUS_TARGET时,check report 会以 JSON 形式位于AD_RESULT_ERR_ACTION_FAILED中。它会在 preflight 前自动等待 5000 ms;ad_last_error_details()接受ad_execute_by_ref_timeout(timeout_ms= 默认 5000 ms,-1= 单次执行)。0默认使用PressKey;其他所有 action(包括focus_fallback)默认使用TypeText。 传入headless(1)可允许在不移动光标的情况下聚焦; 传入AD_POLICY_KIND_FOCUS_FALLBACK(2)可允许物理光标移动和键盘输入。AD_POLICY_KIND_HEADED基于ad_execute_ref_action_exact_with_policy执行相同的 preflight。AdExactRefEntry -
Generation-safe direct APIs。 每个 direct targeting function 都需要 process-generation evidence:、
AdExactRefEntry、AdExactWindowInfo、ad_list_windows_exact以及其他ad_launch_app_exactsymbol。*_exact、AdRefEntry和AdWindowInfo仅作为嵌入 exact structs 的 字段存在。AdSurfaceInfo会保留 surfacead_list_surfaces_exact。从id2 开始,AD_EXACT_SURFACE_INFO_VERSION会列出其 probe 无法读取的 surface kinds(例如AdExactSurfaceInfo.unclassified),否则为 null。 对于已列出的 kind,缺少sheet,menu或sheet表示未知,而不是不存在。 只有 Windows adapter 会填充该字段;macOS 和 Linux 返回 null。menu -
Native handles。和
ad_resolve_element_exact返回ad_find_exact。AdNativeHandle(headless、无 preflight)和ad_execute_action直接作用于该 handle,绕过 ref-action pipeline。 在所有平台上,都必须在ad_execute_action_with_policy之前,使用同一个 adapter 和同一个 thread,通过ad_adapter_destroy释放每个 handle。 它会将ad_free_handle(adapter, &handle)清零,因此第二次调用是 no-op。handle.ptr -
Action results。 对于 activation-chain actions,与 CLI 的
AdActionResult.stepsarray 对应。每个steps包含AdActionStep和label字符串、outcome(mechanism= 1 或AD_STEP_MECHANISM_SEMANTIC_API= 2,仅当AD_STEP_MECHANISM_PHYSICAL_SYNTHETIC时有效), 以及has_mechanism(仅当verified时有效)。has_verified携带与AdActionResult.disposition相同的投递/重试组合。 使用ad_last_error_delivery_semantics释放 result。ad_free_action_result(&out) -
显示发现。 使用前,先调用
AdScreenshotTarget.screen_index。列表顺序就是 screenshot index 顺序。每个ad_list_displays包含稳定的 display ID、边界、是否为主显示器的标志和缩放比例。 使用AdDisplayInfo释放列表。ad_display_list_free -
错误。 调用失败时会设置 thread-local last-error state; 请在下一次失败调用前读取该状态。返回结构化 JSON (
ad_last_error_details()时为 actionability report,ACTION_FAILED时为 candidate summaries),其中可能包含用户屏幕上的 element names、values 和 window titles:不要将其写入共享日志。AMBIGUOUS_TARGET报告失败 action 是否已投递,以及重试是否安全。无效的 enum 判别值会返回ad_last_error_delivery_semantics。参见 error-handling.md。AD_RESULT_ERR_INVALID_ARGS -
线程模型。 Entrypoints 可从任意宿主线程调用。Native handles 必须保留在创建它们的 adapter 和 thread 上;跨线程时应传递限定引用。 桌面修改操作采用与 CLI 相同的跨进程 interaction lease;读取操作不获取 lease, 且具有有限 deadline。跨线程和进程的 read/read、read/mutation 和 mutation/mutation 矩阵参见 threading.md。
-
Tracing。 文件 tracing(与 CLI使用相同的 JSONL 约定; segments 包含
--trace、event、ts_ms和已脱敏字段)仅在以下情况下启用: session 拥有seq的 manifest,且该 manifest 由trace: on(或磁盘上的等效 manifest)在 adapter 创建前生成。仅调用session start不会写入任何内容。每个 OS 进程会向一个 segment 追加数据,路径为ad_adapter_create_with_session, long-lived host 会复用该 segment。~/.agent-desktop/sessions/<id>/trace/<pid>-<procTs>.jsonl(JSON envelope)和ad_trace_show(自包含 HTML 文件)读取 adapter session 的合并时间线;ad_trace_export为limit时表示 CLI 默认值。-1 -
Log callback。会注册一个 process-wide callback 用于诊断,无论 session manifest 是否存在。它以 JSON 形式接收同一 thread 上 后续
ad_set_log_callback(cb)调用内发出的 tracing events;FFI 绝不会安装或替换宿主进程的 global tracing subscriber。ad_*接收一个cblevel (1=ERROR ... 5=TRACE)和一个int32_t,后者仅在调用期间有效。 传入const char *msg可注销 callback。敏感字段值(password、token、text 等) 会被替换为NULL。Callback 不得跨 C ABI unwind: 必须在内部捕获 C++ exceptions 和 Rust panics,否则可能导致宿主进程中止。 它可能同时从多个宿主线程触发,也可能在{"redacted":true}注销后短暂触发: 请确保 callback 及其捕获的任何数据在进程生命周期内始终有效。NULL -
Wait。运行 CLI
ad_wait(adapter, args, &out)command。 设置wait(mode.pause,ms)、AdOptionalU64、mode.element(等待具有该标题的窗口)、mode.window,或mode.text、mode.surfaces.menu、.menu_closed之一; 添加.notificationgroup(predicate、predicate、value、action)以及count/scope.timeout_ms。CLI event wait(scope.app、--event) 未通过 ABI 暴露。调用前先对--window-id进行零初始化,并对照AdWaitArgs/AD_WAIT_ARGS_SIZE验证结构体大小。 输出为ad_wait_args_size()envelope,使用{version, ok, command, data}释放。ad_free_string最多阻塞调用线程ad_waitms。与其并发执行的scope.timeout_ms不会使执行中的 wait 失效。ad_adapter_destroy -
文本输入隐私。 在 macOS 上,focus-fallback 或 headed text insertion 可能会为非 ASCII 文本短暂使用剪贴板。对于敏感文本,如果 target 支持 可设置值,请优先使用搭配
AD_ACTION_KIND_SET_VALUE。AD_POLICY_KIND_HEADLESS -
vs
ad_get_tree_exact。ad_snapshot返回原始的 扁平 breadth-first tree:不含 refs、不持久化 refmap,也不使用 JSON envelope (使用ad_get_tree_exact释放)。将其用于 custom traversal 或 UI inspection。 对于通过ad_free_tree驱动 actions 的 observe-act Agent, 请从ad_execute_by_ref开始。ad_snapshot