Add agent skill package
This commit is contained in:
21
README.md
21
README.md
@@ -1,6 +1,21 @@
|
|||||||
# 内网邮箱 Agent Skill MVP
|
# 内网邮箱 Agent Skill Adapter
|
||||||
|
|
||||||
这是一个本地运行的邮箱 Skill Adapter,面向 Agent 暴露结构化 HTTP 接口,底层使用 POP3 收信、SMTP 发信、SQLite 保存本地索引。
|
这是一个本地运行的邮箱 Skill Adapter,面向 Agent 暴露结构化 HTTP 接口,底层使用 POP3 收信、SMTP 发信、SQLite 保存本地索引。仓库同时包含一个可交给 Agent 使用的 Skill 说明包。
|
||||||
|
|
||||||
|
## Skill 包
|
||||||
|
|
||||||
|
Skill 包位于:
|
||||||
|
|
||||||
|
```text
|
||||||
|
skills/internal-mail-agent/
|
||||||
|
```
|
||||||
|
|
||||||
|
其中:
|
||||||
|
|
||||||
|
- `skills/internal-mail-agent/SKILL.md`:Agent 使用本地邮箱 Adapter 的核心流程和安全规则。
|
||||||
|
- `skills/internal-mail-agent/references/http-api.md`:HTTP API 请求形态、查询参数和示例。
|
||||||
|
|
||||||
|
如果接手环境支持 Codex Skill,可以把 `skills/internal-mail-agent` 复制或安装到对应的 skills 目录;如果不支持,也可以直接把 `SKILL.md` 作为 Agent 工具说明使用。
|
||||||
|
|
||||||
## 重要边界
|
## 重要边界
|
||||||
|
|
||||||
@@ -155,7 +170,7 @@ curl -X POST http://127.0.0.1:8765/send \
|
|||||||
-d '{"to":["bob@example.local"],"bcc":["audit@example.local"],"subject":"直接发送","body":"正文"}'
|
-d '{"to":["bob@example.local"],"bcc":["audit@example.local"],"subject":"直接发送","body":"正文"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
转发邮件。MVP 暂不转发附件,`include_attachments=true` 会返回结构化错误:
|
转发邮件。当前版本暂不转发附件,`include_attachments=true` 会返回结构化错误:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://127.0.0.1:8765/emails/1/forward \
|
curl -X POST http://127.0.0.1:8765/emails/1/forward \
|
||||||
|
|||||||
36
skills/internal-mail-agent/SKILL.md
Normal file
36
skills/internal-mail-agent/SKILL.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
name: internal-mail-agent
|
||||||
|
description: "Use when Codex or another agent needs to work with an internal/local mailbox through the internal-mail-skill REST adapter: syncing POP3 mail, searching and reading email, managing local read/processed/archived/replied state, labels, drafts, replies, forwards, or sending through SMTP when Gmail/IMAP tools are unavailable and the local adapter is running or can be started."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Internal Mail Agent
|
||||||
|
|
||||||
|
Use the local REST adapter as the mailbox backend. The adapter usually runs at `http://127.0.0.1:8765` and stores POP3 mail, local state, labels, drafts, and sent records in SQLite.
|
||||||
|
|
||||||
|
## Before Use
|
||||||
|
|
||||||
|
1. Resolve the base URL from the user, `INTERNAL_MAIL_SKILL_BASE_URL`, or default to `http://127.0.0.1:8765`.
|
||||||
|
2. Call `GET /health`. If it fails, ask the user to start the adapter or start it from the repo with `go run ./cmd/server` when local context allows.
|
||||||
|
3. Treat POP3 server state as one-way input. Read, processed, archived, deleted, replied, labels, drafts, and sent records are local adapter state only.
|
||||||
|
4. Never expose attachment storage paths. Use only `attachment_id`, filename, content type, and size returned by the API.
|
||||||
|
|
||||||
|
## Common Workflow
|
||||||
|
|
||||||
|
- For fresh mailbox work, call `POST /sync` first unless the user explicitly wants local-only results.
|
||||||
|
- For triage, use `GET /emails/search` with filters, then read only the messages needed with `GET /emails/{id}`.
|
||||||
|
- For threads, call `GET /emails/{id}/thread`; the adapter assembles local threads from `Message-ID`, `In-Reply-To`, and `References`.
|
||||||
|
- For organization, use `POST /emails/{id}/mark`, `POST /labels`, and `POST /emails/labels/apply`.
|
||||||
|
- For composing, prefer `POST /drafts` and show the draft summary before sending unless the user explicitly requested immediate send.
|
||||||
|
- For explicit sends, use `POST /drafts/{id}/send` or `POST /send`. The adapter writes every attempt to `sent_messages`.
|
||||||
|
|
||||||
|
## Sending Rules
|
||||||
|
|
||||||
|
- Do not send email unless the user has clearly asked to send, reply, or forward.
|
||||||
|
- Include `to`, `cc`, and `bcc` arrays in requests as needed. Bcc participates in SMTP delivery but must not appear in message headers.
|
||||||
|
- For replies, pass `reply_to_email_id` when creating the draft so the adapter can set `In-Reply-To` and `References` and mark the source email replied after successful send.
|
||||||
|
- If send fails, surface the structured error and do not claim the message was sent.
|
||||||
|
- SMTP delivery does not guarantee the message appears in Webmail "Sent".
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
For request shapes, query parameters, response notes, and curl examples, read `references/http-api.md`.
|
||||||
4
skills/internal-mail-agent/agents/openai.yaml
Normal file
4
skills/internal-mail-agent/agents/openai.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Internal Mail Agent"
|
||||||
|
short_description: "Use the local POP3/SMTP mail adapter as an agent skill."
|
||||||
|
default_prompt: "Search my internal mail for recent unread messages and summarize what needs a reply."
|
||||||
135
skills/internal-mail-agent/references/http-api.md
Normal file
135
skills/internal-mail-agent/references/http-api.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Internal Mail Adapter HTTP API
|
||||||
|
|
||||||
|
Default base URL: `http://127.0.0.1:8765`.
|
||||||
|
|
||||||
|
All errors use:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"error":{"code":"code","message":"message","details":{}}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health and Sync
|
||||||
|
|
||||||
|
`GET /health`
|
||||||
|
|
||||||
|
Returns service status, version, database status, account ID, and current time.
|
||||||
|
|
||||||
|
`POST /sync`
|
||||||
|
|
||||||
|
Fetches POP3 messages with UIDL de-duplication. Does not delete server mail. Single-message failures appear in `errors[]` and do not stop the whole sync.
|
||||||
|
|
||||||
|
## Search and Read
|
||||||
|
|
||||||
|
`GET /emails/search`
|
||||||
|
|
||||||
|
Supported query parameters:
|
||||||
|
|
||||||
|
- `q`
|
||||||
|
- `from`
|
||||||
|
- `to`
|
||||||
|
- `after`
|
||||||
|
- `before`
|
||||||
|
- `has_attachment`
|
||||||
|
- `read`
|
||||||
|
- `processed`
|
||||||
|
- `archived`
|
||||||
|
- `replied`
|
||||||
|
- `limit`
|
||||||
|
- `offset`
|
||||||
|
|
||||||
|
Returns summaries only: subject, sender, recipients, dates, snippet, local status, labels, and attachment count.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "http://127.0.0.1:8765/emails/search?q=合同&from=alice@example.local&has_attachment=true&limit=20"
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /emails/{id}`
|
||||||
|
|
||||||
|
Returns full local email detail: subject, from, to, cc, date, body text, body HTML, attachments, local status, labels, and thread headers. Attachments expose `attachment_id`, filename, content type, content ID, and size; do not expect or request filesystem paths.
|
||||||
|
|
||||||
|
`GET /emails/{id}/thread`
|
||||||
|
|
||||||
|
Returns local thread summaries. A message joins the thread when its `In-Reply-To` or `References` contains another local message's `Message-ID`.
|
||||||
|
|
||||||
|
## Local State and Labels
|
||||||
|
|
||||||
|
`POST /emails/{id}/mark`
|
||||||
|
|
||||||
|
Body fields are optional booleans:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"read":true,"processed":true,"archived":false,"deleted":false,"replied":true}
|
||||||
|
```
|
||||||
|
|
||||||
|
These states are local only and are not synchronized back to POP3.
|
||||||
|
|
||||||
|
`POST /labels`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"name":"待处理","color":"#336699"}
|
||||||
|
```
|
||||||
|
|
||||||
|
`POST /emails/labels/apply`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"email_ids":[1],"label_ids":[1],"op":"add"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `"op":"remove"` to remove labels.
|
||||||
|
|
||||||
|
## Drafts and Sending
|
||||||
|
|
||||||
|
`POST /drafts`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"to":["bob@example.local"],
|
||||||
|
"cc":[],
|
||||||
|
"bcc":[],
|
||||||
|
"subject":"测试",
|
||||||
|
"body":"你好",
|
||||||
|
"reply_to_email_id":1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`reply_to_email_id` is optional. Use it for replies.
|
||||||
|
|
||||||
|
`GET /drafts`
|
||||||
|
|
||||||
|
Lists local drafts.
|
||||||
|
|
||||||
|
`PATCH /drafts/{id}`
|
||||||
|
|
||||||
|
Uses the same body shape as `POST /drafts`.
|
||||||
|
|
||||||
|
`POST /drafts/{id}/send`
|
||||||
|
|
||||||
|
Sends a draft by SMTP. On success, the adapter marks the draft `sent`, writes `sent_messages(status=sent)`, and marks the source email replied when the draft is a reply. On failure, it writes `sent_messages(status=failed,error_message=...)` and leaves the draft unsent.
|
||||||
|
|
||||||
|
`POST /send`
|
||||||
|
|
||||||
|
Direct send. Uses the same request shape as `POST /drafts`, internally creating and sending a draft.
|
||||||
|
|
||||||
|
`POST /emails/{id}/forward`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"to":["bob@example.local"],
|
||||||
|
"cc":[],
|
||||||
|
"bcc":[],
|
||||||
|
"subject":"Fwd: 原邮件",
|
||||||
|
"body":"请看下面原文",
|
||||||
|
"include_attachments":false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Attachment forwarding is reserved for a later version. `include_attachments=true` returns a structured error.
|
||||||
|
|
||||||
|
## Agent Notes
|
||||||
|
|
||||||
|
- Prefer summaries for scanning and read full bodies only when needed.
|
||||||
|
- Do not infer server-side read/unread from local status.
|
||||||
|
- Do not claim SMTP-sent messages appear in Webmail Sent.
|
||||||
|
- If `/sync` returns message-level errors, report counts and the affected UID/server ID instead of treating the whole sync as failed.
|
||||||
Reference in New Issue
Block a user