Status: ✅ READY TO START (2026-04-27)
NyxID 侧基础设施 verified,aevatar 侧可以直接开 PR;mainnet smoke test 等 ops 配完 Twitter app credentials 再做。
| Prerequisite |
Status |
NyxID twitter provider seeded (provider_service.rs:405-450, OAuth 2.0 + PKCE, scopes 含 tweet.write) |
✅ |
NyxID api-twitter service seeded (provider_service.rs:1725-1735, base https://api.x.com/2, bearer 自动注入) |
✅ |
NyxID proxy 透明路由 (proxy.rs:483-496, ANY /api/v1/proxy/s/{slug}/{*path}) |
✅ |
aevatar NyxIdApiClient.ProxyRequestAsync wrapper(已被 daily_report 用) |
✅ |
aevatar OutboundConfig + 永久 NyxID API key 机制(#185 已 ship) |
✅ |
aevatar PreflightGitHubProxyAsync 模式可直接 fork 出 PreflightTwitterProxyAsync (#418) |
✅ |
mainnet NyxID 部署给 twitter provider 配 Twitter app client_id/secret |
⚠️ ops 动作 — 不阻塞 aevatar 代码 ship |
client_id_encrypted / client_secret_encrypted 在 NyxID provider 记录里默认是 None — 部署侧需要去 https://developer.x.com 注册一个 Twitter app(OAuth 2.0 + PKCE,redirect_uri 指 NyxID 的 callback),通过 NyxID 管理面 / admin API(backend/src/handlers/providers.rs)写入。这是部署 ops 动作;aevatar PR 可以先 merge,等 ops 配完再 mainnet smoke test。
Goal
Wire social_media template's post-approval publish step:approve 后 aevatar 调 NyxID api-twitter proxy 发推,把结果反馈到 Feishu。跟 daily_report 调 api-github 同模式,不引入 direct X SDK。
Background
PR #193 已 ship social_media 模板的:
- scheduled workflow run
- Feishu approval card delivery
- approve/reject resolution back into Aevatar
- approval/rejection feedback back to Feishu
但 approve 之后没有实际 publish 到 X。这个 issue 补上最后一段。
Implementation steps
Step 1 · PreflightTwitterProxyAsync (~半天)
Mirror AgentBuilderTool.PreflightGitHubProxyAsync (added in #418 / #421):
// 在 social_media agent 创建时调用
GET /api/v1/proxy/s/api-twitter/users/me with the freshly minted agent api-key
返回分类:
落点:agents/Aevatar.GAgents.ChannelRuntime/AgentBuilderTool.cs(或在 PR #451 拆完后落到 Aevatar.GAgents.Authoring)。
Step 2 · Publish action in workflow approval-resolution path (~半天)
WorkflowAgentGAgent 在 social_media approval 通过分支里加:
var body = JsonSerializer.Serialize(new { text = approvedContent });
var result = await nyxIdApiClient.ProxyRequestAsync(
apiKey: outboundConfig.NyxApiKey,
serviceSlug: \"api-twitter\",
path: \"tweets\",
method: \"POST\",
body: body,
ct);
Twitter v2 成功响应 (HTTP 201):
{ \"data\": { \"id\": \"1234567890\", \"text\": \"...\" } }
成功 → 提取 data.id,可选 users/me 拿 handle,反馈 已发布: https://x.com/{handle}/status/{id} 到 Feishu。
Step 3 · 错误分类 + Feishu surfacing (~半天)
类似 SkillRunnerGAgent.SendOutputAsync 的失败通知模式:
| HTTP |
Lark 反馈文案 |
是否重试 |
| 201 |
已发布: https://x.com/{handle}/status/{id} |
— |
| 401 |
Twitter OAuth 过期,去 https://nyxid.../providers/twitter 重新授权 |
不重试 |
| 403 |
Twitter scope 不足(tweet.write 缺失)— 不应发生,请联系 ops |
不重试 |
| 429 |
Twitter rate limit,{retry_after}s 后自动重试 |
1 次 |
| 5xx |
Twitter 服务异常,已重试 N 次仍失败 |
最多 2 次 backoff |
承袭 #412 的"actionable last_error"原则:每个错误码自带恢复路径文字。
Step 4 · 测试 (~半天)
Mock NyxID proxy api-twitter 返回 success / 401 / 403 / 429 / 5xx,验证:
- happy path:approval → publish → feishu surfacing 含 tweet URL
- 401:feishu 显示 OAuth 重连提示,approval 状态机不卡住
- 429:retry 1 次后失败显示 rate limit 文案
- approval resolution 的现有路径不受影响(
PR #193 行为保留)
落点:test/Aevatar.GAgents.ChannelRuntime.Tests/ (拆包后落到 test/Aevatar.GAgents.Authoring.Tests/)。
NyxID OAuth 用户流程(前端体验,不在本 issue scope 但要知道)
用户首次创建 social_media agent 时:
- aevatar
PreflightTwitterProxyAsync → 401 → 提示 user 去 NyxID 连 Twitter
- user 在 NyxID 走 OAuth:
https://x.com/i/oauth2/authorize?...&scope=tweet.read+tweet.write+users.read+offline.access&...
- 授权回来,NyxID 存 access_token + refresh_token(per-user,credential_mode: user)
- user 回 aevatar 重试创建,pre-flight 200 → agent 创建成功
- 之后每次 publish,NyxID 自动用 user 的 token 调 Twitter;token 过期时 refresh 也由 NyxID 处理
aevatar 端完全不持有 user 的 Twitter token — 全程通过 api-key proxy。符合 NyxID #505 capability-broker 定位(aevatar #375 RFC:线上零 secret material)。
Acceptance
Non-goals
- 不引入 direct Twitter / X SDK
- 不扩展到 group-chat 创建流
- 不改
daily_report 模板
- 不在本 issue 配 Twitter app credentials(ops 动作,单独 track)
- 不做 multi-tweet thread / 媒体附件 / poll(v1 只发 plain text;后续 issue 扩展)
Estimated effort
~1.5-2 天 working time(不含等 ops 配 credentials):
- Step 1 PreflightTwitterProxyAsync: 0.5 天
- Step 2 publish action: 0.5 天
- Step 3 error 分类 + Feishu surfacing: 0.5 天
- Step 4 测试: 0.5 天
Related
Status: ✅ READY TO START (2026-04-27)
NyxID 侧基础设施 verified,aevatar 侧可以直接开 PR;mainnet smoke test 等 ops 配完 Twitter app credentials 再做。
twitterprovider seeded (provider_service.rs:405-450, OAuth 2.0 + PKCE, scopes 含tweet.write)api-twitterservice seeded (provider_service.rs:1725-1735, basehttps://api.x.com/2, bearer 自动注入)proxy.rs:483-496,ANY /api/v1/proxy/s/{slug}/{*path})NyxIdApiClient.ProxyRequestAsyncwrapper(已被 daily_report 用)OutboundConfig+ 永久 NyxID API key 机制(#185 已 ship)PreflightGitHubProxyAsync模式可直接 fork 出PreflightTwitterProxyAsync(#418)twitterprovider 配 Twitter appclient_id/secretclient_id_encrypted/client_secret_encrypted在 NyxID provider 记录里默认是None— 部署侧需要去 https://developer.x.com 注册一个 Twitter app(OAuth 2.0 + PKCE,redirect_uri 指 NyxID 的 callback),通过 NyxID 管理面 / admin API(backend/src/handlers/providers.rs)写入。这是部署 ops 动作;aevatar PR 可以先 merge,等 ops 配完再 mainnet smoke test。Goal
Wire
social_mediatemplate's post-approval publish step:approve 后 aevatar 调 NyxIDapi-twitterproxy 发推,把结果反馈到 Feishu。跟daily_report调api-github同模式,不引入 direct X SDK。Background
PR #193 已 ship
social_media模板的:但 approve 之后没有实际 publish 到 X。这个 issue 补上最后一段。
Implementation steps
Step 1 ·
PreflightTwitterProxyAsync(~半天)Mirror
AgentBuilderTool.PreflightGitHubProxyAsync(added in #418 / #421):返回分类:
twitter_oauth_required,hint 用户去 NyxID 重新授权tweet.write),但写防御性提示twitter_proxy_access_denied落点:
agents/Aevatar.GAgents.ChannelRuntime/AgentBuilderTool.cs(或在 PR #451 拆完后落到Aevatar.GAgents.Authoring)。Step 2 · Publish action in workflow approval-resolution path (~半天)
WorkflowAgentGAgent在social_mediaapproval 通过分支里加:Twitter v2 成功响应 (HTTP 201):
{ \"data\": { \"id\": \"1234567890\", \"text\": \"...\" } }成功 → 提取
data.id,可选users/me拿 handle,反馈已发布: https://x.com/{handle}/status/{id}到 Feishu。Step 3 · 错误分类 + Feishu surfacing (~半天)
类似
SkillRunnerGAgent.SendOutputAsync的失败通知模式:已发布: https://x.com/{handle}/status/{id}Twitter OAuth 过期,去 https://nyxid.../providers/twitter 重新授权Twitter scope 不足(tweet.write 缺失)— 不应发生,请联系 opsTwitter rate limit,{retry_after}s 后自动重试Twitter 服务异常,已重试 N 次仍失败承袭 #412 的"actionable last_error"原则:每个错误码自带恢复路径文字。
Step 4 · 测试 (~半天)
Mock NyxID proxy
api-twitter返回 success / 401 / 403 / 429 / 5xx,验证:PR #193行为保留)落点:
test/Aevatar.GAgents.ChannelRuntime.Tests/(拆包后落到test/Aevatar.GAgents.Authoring.Tests/)。NyxID OAuth 用户流程(前端体验,不在本 issue scope 但要知道)
用户首次创建 social_media agent 时:
PreflightTwitterProxyAsync→ 401 → 提示 user 去 NyxID 连 Twitterhttps://x.com/i/oauth2/authorize?...&scope=tweet.read+tweet.write+users.read+offline.access&...aevatar 端完全不持有 user 的 Twitter token — 全程通过 api-key proxy。符合 NyxID #505 capability-broker 定位(aevatar #375 RFC:线上零 secret material)。
Acceptance
PreflightTwitterProxyAsync在 social_media agent 创建时检查 Twitter OAuth;401 透传twitter_oauth_requiredPOST /api/v1/proxy/s/api-twitter/tweetsbody{\"text\":...}发布https://x.com/{handle}/status/{id});handle 从users/me拿Non-goals
daily_report模板Estimated effort
~1.5-2 天 working time(不含等 ops 配 credentials):
Related
PreflightGitHubProxyAsync模式可直接 forkprovider_service.rs:405-450— twitter provider seedprovider_service.rs:1725-1735— api-twitter service seedproxy.rs:483-496— proxy routing