Metadata-Version: 2.5
Name: tabapp-agent-sdk
Version: 0.1.0
Summary: Tab Agent Python SDK：Tab + Agent/Run，覆盖 Open API 与 A2A。
Project-URL: Homepage, https://www.xtabapp.com
Project-URL: Documentation, https://github.com/tabapp/tabapp/tree/main/packages/agent-sdk-py
Project-URL: Source, https://github.com/tabapp/tabapp/tree/main/packages/agent-sdk-py
Author: tabapp
License: MIT
Keywords: a2a,agent,sdk,tab
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Requires-Dist: httpx<1,>=0.26
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Description-Content-Type: text/markdown

# tabapp-agent-sdk

Python Agent SDK。给小抄这类 Python Host 调 Tab。浏览器不要用这个包，Web Composer 是 `@tabapp/agent-sdk/composer`。

形状对齐 Cursor Agent SDK：`Agent` → `Run`。控制面是 `Tab`。底层协议（Open API + A2A）都封装在里面，Host 不要再手写 HTTP。

```python
from tabapp_agent_sdk import Agent, Tab, AgentSdkError

tab = Tab("https://www.xtabapp.com", api_key=tak, user_jwt=jwt)
experts = await tab.experts.list()
hook = await tab.hooks.create(expert_id, name="xiaochao", source="xiaochao")

async with await Agent.connect(
    base_url="https://www.xtabapp.com",
    expert_id=expert_id,
    hook_url=hook["hook_url"],
    hook_token=hook["token"],
    a2a_url=hook["a2a_url"],
    signing_secret=hook.get("signing_secret"),
) as agent:
    run = await agent.send(
        "请校对这篇稿",
        event="draft.review",
        payload={"draft_id": "d_1", "account_id": "sp_1"},
        callback_url="https://weixin.xtabapp.com/api/agent-runs/callback",
        thread_key="account:sp_1",
        idempotency_key="xiaochao:draft:d_1:v2",
    )
    async for event in run.stream():
        print(event.state, event.message, event.tool)
    result = await run.wait()
    if result.status == "error":
        raise SystemExit(2)
```

`Agent.create` 是 `connect` 的别名：Tab Open API 还不能创建专家，连接的是已有专家 + Hook。

`Agent.prompt(...)` 是一次性：connect → send → wait → close。

`Agent.resume(task_id, ...)` 挂回已有任务，返回 `Run`。

`Run.stream` / `Run.wait` 会在 Tab 约 15 秒断开后自动重订，直到终态、`input-required` / `auth-required`，或 `max_seconds`。单次 `tasks/subscribe` 仍可通过 `AgentClient.subscribe_task` 拿到。

## 和 Cursor SDK 的对应

| Cursor | Tab |
| --- | --- |
| `Agent.create` | `Agent.connect` / `Agent.create`（绑已有专家） |
| `Agent.resume` | `Agent.resume(task_id, …)` |
| `Agent.prompt` | `Agent.prompt` |
| `agent.send` | `agent.send` / `agent.trigger` |
| `run.stream` / `run.wait` | 同名；内部消化 15 秒 SSE |
| `run.cancel` | `run.cancel` |
| `result.status == "error"` | 任务已执行后失败 |
| `CursorAgentError` | `AgentSdkError`（没跑起来：鉴权 / 网络） |
| `Cursor.models` | `Tab.keys` / `Tab.experts` / `Tab.hooks` |

创建、发布、部署专家仍未进入 Open API：`tab.experts.create|publish|deploy` 会抛 `unimplemented`。

## 控制面

```python
await tab.keys.create(label="xiaochao")
await tab.keys.list()
await tab.keys.rotate(key_id)
await tab.keys.revoke(key_id)

await tab.experts.list()
await tab.experts.get(agent_id)
await tab.experts.agent_card(agent_id)

await tab.hooks.create(agent_id, name="xiaochao", continuity_mode="thread")
await tab.hooks.list(agent_id)
await tab.hooks.get(hook_id)
await tab.hooks.update(hook_id, {"enabled": False})
await tab.hooks.rotate(hook_id)
await tab.hooks.deliveries(hook_id)
await tab.hooks.delete(hook_id)
```

## A2A

任务操作走 A2A。只读工单页用 share token：

```python
async for frame in tab.embed.subscribe(share_token):
    ...
await tab.embed.answer(share_token, "改标题")
```

回调验签：

```python
from tabapp_agent_sdk import verify_callback

if not verify_callback(signing_secret, raw_body, signature, timestamp):
    raise AgentSdkError("tab_unauthorized", "回调签名无效。", 401)
```

## 小抄现有调用

`AgentClient` 保留原来的方法签名（`create_key` / `list_experts` / `trigger` / `subscribe_task` …），内部走同一套协议实现。新代码请改用 `Tab` + `Agent`。
