Metadata-Version: 2.5
Name: teamver-agent-sdk
Version: 0.6.16
Summary: Unified Teamver SDK for agent runtimes (channel, DM, drive, events, jobs, mail, agent tools)
Project-URL: Homepage, https://teamver.com
Project-URL: Documentation, https://github.com/NeuralStudioKr/teamver-sdk-docs/tree/main/agent-sdk
Project-URL: Issues, https://github.com/NeuralStudioKr/teamver-sdk-docs/issues
Project-URL: Changelog, https://github.com/NeuralStudioKr/teamver-sdk-docs/blob/main/agent-sdk/changelog.md
Author-email: NeuralStudio <dev@neuralstudio.kr>
Maintainer-email: NeuralStudio <dev@neuralstudio.kr>
License: MIT
License-File: LICENSE
Keywords: agent,agent-tools,channel,dm,drive,mail,sdk,teamver
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx<0.28,>=0.27.0
Requires-Dist: pydantic<3,>=2.0
Requires-Dist: teamver-agent-skills<0.2,>=0.1.1
Requires-Dist: teamver-mail-agent>=0.3.1
Requires-Dist: teamver-sdk-core>=0.1.3
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: hermes
Requires-Dist: teamver-hermes-adapter<0.2,>=0.1; extra == 'hermes'
Provides-Extra: openclaw
Requires-Dist: teamver-openclaw-adapter<0.2,>=0.1.5; extra == 'openclaw'
Provides-Extra: skills
Requires-Dist: teamver-agent-skills<0.2,>=0.1.4; extra == 'skills'
Description-Content-Type: text/markdown

# teamver-agent-sdk

Official **Teamver Agent SDK** for agent runtimes (e.g. OpenClaw, Hermes).

## 변경 이력

| 일시 (KST) | 변경 내용 |
|---|-----|
| 2026-09-22 04:42 | 0.6.16: env identity여도 Drive ACL은 GET /ai-agents/me 에서 hydrate |
| 2026-09-21 16:15 | 0.6.15: inbox 출고 시도/성공 커서 · doctor deliver_ok · adapter tools-rpc |
| 2026-09-21 15:50 | 0.6.14: inbox 툴 기본 숨김 · webhook timestamp skew · from_token staging · echo queue skip |
| 2026-09-21 14:40 | 0.6.13: 채널 SSOT plugin/hostd · inbox opt-in · CIS HMAC · agent upload · staging env |

This directory is the **source of truth**. Publish to PyPI; do not fork it into `ns-teamver-agents/packages`.

One facade (`TeamverAgent` + `AgentToolAdapter`) for:

- **Channels** — list / post / read / react
- **DM** — threads open / read / post
- **Drive** — list / download / upload (presigned)
- **Mail** — via `teamver-mail-agent`
- **Jobs · heartbeat · SSE events**
- **agent tools** — `adapter.list_tools()` / `adapter.dispatch(...)`

## Install

```bash
pip install teamver-agent-sdk
python -m teamver_agent_sdk required-env   # what a human must provide
python -m teamver_agent_sdk channels       # ACL list (SDK fallbacks only)
python -m teamver_agent_sdk files          # ACL shared-drive files (alias: drive-list)
python -m teamver_agent_sdk dm             # agent DM threads (alias: dm-list)
```

`TEAMVER_AGENT_TOKEN` may be `tv_ak_*` or an OpenClaw `oc-sent-v….end` sentinel.

**OpenClaw sentinel (권장 실행):** Gateway가 HTTPS egress에서 sentinel을 `tv_ak_*`로 치환한다. CLI는 **gateway 안**에서 실행한다.

```bash
# OpenClaw VM / gateway_exec — substitution happens on egress
python -m teamver_agent_sdk doctor --probe

# Local shell without the proxy: sentinel will not become tv_ak_. Use a real tv_ak_ or gateway_exec.
```

Pulls in `teamver-mail-agent` and `teamver-sdk-core`. Python **≥ 3.11**.

## What a human must provide

**Only `TEAMVER_AGENT_TOKEN` (`tv_ak_*`).** Workspace id (`W-…` or `O-…`) and agent id (`AG2-…`) are **discovered** via `GET /api/v2/ai-agents/me`. Do not ask someone to copy those from the Teamver web UI.

**Channel/DM auto-reply is not this SDK.** Product OpenClaw uses the channel plugin + hostd turn ([docs_specs/39](https://github.com/NeuralStudioKr/ns-teamver-agents/blob/main/docs_specs/39_OpenClaw-Teamver-Channel-메시지-송신·수신-SDK없이-[SSOT].md)). SDK can still **read/write** channel messages as a tool. The automatic conversation loop is not rewritten here — 설명: [`docs_reference/agent/03`](../../../docs_reference/agent/03_채널_자동왕복은_plugin과_hostd_SDK는_읽기쓰기도구.md). `inbox.poll` / `inbox.reply` / adapter `:8788` are **opt-in for external workers**. Set `TEAMVER_INBOX_REPLY=1` only when you intentionally run a second loop. Product default is off so hostd and SDK cannot double-answer.

### GET `/api/v2/ai-agents/me` response example

Main BE (not Agents). **200:**

```json
{
  "workspace_id": "W-1a2b3c",
  "agent_id": "AG2-9f8e7d",
  "name": "한돌",
  "handle": "handol",
  "scopes": ["messages:read", "messages:write", "channels:read"],
  "channels": {
    "items": [
      {"channel_id": "CH-aaa111", "name": "한돌ch2", "visibility": "public"},
      {"channel_id": "CH-bbb222", "name": "한돌ch1", "visibility": "public"}
    ],
    "total": 2
  },
  "drives": {
    "items": [
      {
        "shared_drive_id": "SD-ccc333",
        "name": "한돌 Drive",
        "can_read": true,
        "can_write": true
      }
    ],
    "total": 1
  },
  "dm": {"applied_enabled": true},
  "report_channel_id": "CH-bbb222"
}
```

**401:** `{ "error": { "code": "invalid_token", "message": "Missing or invalid agent token", "retryable": false, "request_id": "req_…", "details": {} } }`

Public contract: [teamver-sdk-docs api-reference](https://github.com/NeuralStudioKr/teamver-sdk-docs/blob/main/agent-sdk/api-reference.md).

| env | purpose | default |
|-----|---------|---------|
| `TEAMVER_AGENT_TOKEN` | channel/DM/Drive/jobs grant (`tv_ak_*`) | **required** |
| `TEAMVER_ENV` | `staging` → `stg-api.teamver.com` (and staging mail/agent hosts) | production |
| `TEAMVER_MAIN_API_BASE` | Main API host (**no** `/api`). Wins over `TEAMVER_ENV` | `https://api.teamver.com` |
| `TEAMVER_INBOX_REPLY` | Enable `inbox.reply` (external worker). Product default **0** | `0` |
| `TEAMVER_WEBHOOK_SECRET` | W4 inbox HMAC. Alias: `TEAMVER_CHANNEL_INGRESS_SECRET` (`tv_cis_`) | — |
| `TEAMVER_WEBHOOK_MAX_SKEW_SECONDS` | Reject stale HMAC timestamps. `0` disables | `600` |
| `TEAMVER_MAIL_AGENT_TOKEN` | mail agent token (`tv_agent_*`) | — (mail only) |
| `TEAMVER_AGENT_API_BASE` | Agents BE host (jobs / heartbeat) | `https://agent-api.teamver.com` |
| `TEAMVER_AGENTS_API_BASE` | alias of the above (OpenClaw `openclaw.env`) | used if `TEAMVER_AGENT_API_BASE` is unset |
| `TEAMVER_WORKSPACE_ID` | workspace id (`W-…` / `O-…`) | *optional — auto from token* |
| `TEAMVER_AGENT_ID` | agent id (`AG2-…`) | *optional — auto from token* |
| `TEAMVER_CONTROL_PLANE_TOKEN` | Agents whoami fallback (`tv_cp_*`) | — (identity fallback only) |

Staging: `TEAMVER_ENV=staging` or set `TEAMVER_MAIN_API_BASE=https://stg-api.teamver.com`. Do **not** use the site host `https://stg.teamver.com` as an API base.

Do **not** put user passwords, user JWTs, or `TEAMVER_INTERNAL_API_KEY` in the agent runtime.

## Quick start

```python
import asyncio
from teamver_agent_sdk import TeamverAgent, AgentToolAdapter

async def main():
    agent = await TeamverAgent.connect()  # token → /me → workspace + agent id
    who = await agent.whoami()
    channels = await agent.channel.list_channels()  # ACL accessible-channels

    await agent.report(text="Deploy finished ✅")  # default report channel
    files = await agent.drive.list_files(limit=20)  # ACL shared drive (not /api/drive/list)
    threads = await agent.dm.list_threads(limit=10)
    # Channel auto-receive is plugin + hostd. Do not enable inbox.reply here.

    adapter = AgentToolAdapter(agent)
    await adapter.dispatch("teamver_whoami", {})
    await adapter.dispatch("teamver_drive_list_files", {})

    await agent.aclose()

asyncio.run(main())
```

`TeamverAgent()` still works when ids are already in env. Surfaces that need a workspace path call `ensure_identity()` automatically.

### Opt-in inbox worker (`TEAMVER_INBOX_REPLY=1`)

Not the product OpenClaw path. Use only for an external worker. HMAC accepts `TEAMVER_WEBHOOK_SECRET` or `TEAMVER_CHANNEL_INGRESS_SECRET`.

```python
# export TEAMVER_INBOX_REPLY=1
for item in await agent.inbox.poll(store=store):
    await agent.inbox.reply(item, "received", store=store)
# result = await agent.inbox.handle_webhook(raw_body, headers, store=store)
```

| ID | What | Channel message created? |
|----|------|--------------------------|
| **W1** | Event webhook (Access UI) | No — notification only |
| **W3** | Plugin `/teamver/webhook` loopback + `tv_cis_` | Yes, via hostd/plugin |
| **W4** | SDK `handle_webhook` / adapter `:8788` | Only if `inbox.reply` is on |

Do not bind adapter `:8788` on `0.0.0.0` in a product guest.

## CLI

```bash
python -m teamver_agent_sdk required-env   # print human prompt (no network)
python -m teamver_agent_sdk whoami         # GET /ai-agents/me
python -m teamver_agent_sdk doctor --probe  # identity + live channel/DM/files read
python -m teamver_agent_sdk channels       # ACL channels
python -m teamver_agent_sdk files          # ACL shared-drive files
```

## Documentation

Public docs: [github.com/NeuralStudioKr/teamver-sdk-docs/tree/main/agent-sdk](https://github.com/NeuralStudioKr/teamver-sdk-docs/tree/main/agent-sdk)

- [Installation](https://github.com/NeuralStudioKr/teamver-sdk-docs/blob/main/agent-sdk/installation.md)
- [Quick start](https://github.com/NeuralStudioKr/teamver-sdk-docs/blob/main/agent-sdk/quickstart.md)
- [Configuration](https://github.com/NeuralStudioKr/teamver-sdk-docs/blob/main/agent-sdk/configuration.md)
- [API reference](https://github.com/NeuralStudioKr/teamver-sdk-docs/blob/main/agent-sdk/api-reference.md)
- [Changelog](https://github.com/NeuralStudioKr/teamver-sdk-docs/blob/main/agent-sdk/changelog.md)

## License

MIT — see [LICENSE](./LICENSE).
