背景
Day One (04-25) 目标:外部用户在飞书私聊 bot 创建持久运行的自动化 agent。成功标准:≥5 agent 运行 48h+,≥3 用户无人工指导完成创建。
关联:#182 (Day One spec)、#183 (Owner 自主目标)、NyxID#306 (飞书卡片 outbound 发送 — ✅ 已部署 PR #314)
核心约束
- 用户不学习任何新工具,唯一界面是飞书私聊 + 必要的浏览器跳转(OAuth)
- K8s 部署,无本地磁盘
- 非技术用户,UX 是生死线("上次强推技术工具,同事集体离职")
- Day One 只支持私聊创建 agent(群聊需要 org credential,安全隐患,post-Day One 做)
架构方案(收敛版,含全部 review 修正 + 第三轮收口)
架构图
飞书 webhook
│
▼
Aevatar ChannelCallbackEndpoints (已有, /{platform}/callback/{registrationId})
│ 处理: im.message.receive_v1 (已有) + card.action.trigger (需扩展)
│
├─ card_action 且有 run_id+step_id?
│ → 确定性路由: DispatchWorkflowResumeAsync() → /api/workflows/resume
│ → 不过 LLM,3 秒内返回
│
└─ 普通消息 或 card_action 无 run_id
→ DispatchToUserActorAsync() (已有)
→ ChannelUserGAgent (已有, per-sender actor, 持有 State.NyxidAccessToken)
→ NyxIdChatGAgent (管家, 已有)
管家 = NyxID-chat GAgent + AgentBuilderTool
│
├─ Tools:
│ ├─ AgentBuilderTool (create/list/status/delete) [新建]
│ ├─ nyxid_providers (OAuth 连接, action=connect_oauth) [已有]
│ ├─ nyxid_api_keys (创建 non-expiring API Key) [已有]
│ ├─ nyxid_proxy (API 代理) [已有]
│ └─ use_skill (skill 加载) [已有]
│
├─ create_agent(template=daily_report)
│ → nyxid_api_keys 创建 non-expiring API Key (scope=proxy)
│ → 创建 SkillRunnerGAgent (state 包含 OutboundConfig)
│ → 注册 Orleans Reminder (定时触发)
│ → 不创建新 channel registration
│
└─ create_agent(template=social_media)
→ nyxid_api_keys 创建 non-expiring API Key
→ IScopeWorkflowCommandPort 创建 WorkflowGAgent (definition only)
→ SchedulerGAgent 持有 timer → fire → 创建 WorkflowRunGAgent
Agent Outbound (发飞书消息/卡片):
agent 自持 OutboundConfig { ConversationId, NyxProviderSlug, NyxApiKey }
→ NyxIdApiClient.ProxyRequestAsync(apiKey, slug, path, ...)
→ NyxID api-lark-bot (outbound proxy, token_exchange)
→ 飞书 open-apis/im/v1/messages
不走 channel registration,不走 ChannelUserGAgent
关键架构决策
| # |
决策 |
选择 |
依据 |
| 1 |
消息入站路径 |
飞书直入 Aevatar ChannelCallbackEndpoints |
system-prompt.md: "Webhooks go directly to Aevatar, NOT through NyxID" |
| 2 |
NyxID 角色 |
仅 outbound proxy (存 bot credentials + 代理 API 调用) |
同上 |
| 3 |
card_action 路由 |
ChannelCallbackEndpoints 轻量分流: 有 run_id+step_id → workflow resume (确定性); 无 → 管家 (LLM) |
飞书 3 秒超时, resume 是确定性路由不需要 LLM |
| 4 |
模板 1 执行 |
SkillRunnerGAgent (新 GAgent 类型, : AIGAgentBase) |
/daily 是 Claude Code skill, 需 LLM agent loop |
| 5 |
/daily 适配 |
改写为 Aevatar-native skill (gh api → NyxIdProxyTool) |
UseSkillTool 只返回 SKILL.md 给模型, 不是 Claude Code runtime |
| 6 |
模板 2 定时 |
SchedulerGAgent 持有 timer, fire → 创建 WorkflowRunGAgent |
WorkflowGAgent 只承载 definition, WorkflowRunGAgent 承载执行 |
| 7 |
人机交互 |
IHumanInteractionPort (渲染+发送+关联) |
需完成 event observation → 卡片发送 → resume 关联全链路 |
| 8 |
resume 路径 |
/api/workflows/resume (actorId + runId + stepId) |
ChatEndpoints.cs 中的实际路径 |
| 9 |
Token 续期 |
NyxID API Key (永不过期) 存入 agent OutboundConfig |
不复用 channel_registrations(它强制取 session token) |
| 10 |
Agent outbound 模型 |
agent 自持 OutboundConfig, 直接用 NyxIdApiClient 发消息 |
一个 bot 一个 registration, 多 agent 共用; 不为每个 agent 创建新 registration |
| 11 |
LLM Provider |
Org 预配置 (用户零配置) |
NyxID org-shared credentials, LLM Gateway 自动路由 |
| 12 |
飞书卡片流程 |
表单流 (一张卡片收集参数) |
减少消息数, 符合零培训原则 |
| 13 |
用户身份 |
per-user NyxID (私聊), ChannelUserGAgent.State.NyxidAccessToken |
已有机制; 群聊 agent 创建 Day One 不开放 |
新增组件
1. SkillRunnerGAgent
位置: src/Aevatar.AI.Core/SkillRunnerGAgent.cs
继承: AIGAgentBase<SkillRunnerGAgentState>
State:
skill_content // Aevatar-native skill 定义
schedule_cron // cron 表达式
outbound_config // OutboundConfig { ConversationId, NyxProviderSlug, NyxApiKey, OwnerNyxUserId }
last_run_time, last_output, error_count
Agent Loop:
Timer fire → self-message: SkillExecutionTriggeredEvent (事件化)
→ 构建 ChatRequest (system=skill content, user="Execute now. Date: {today}")
→ ChatStreamAsync (复用 AIGAgentBase LLM+tool 能力)
→ Tools: NyxIdProxyTool (api-github, api-feishu-bot via NyxID)
→ Max 20 iterations, 5 min timeout
→ 失败: 重试 1 次 (30s backoff), 仍失败 → 通知用户
→ 输出 → NyxIdApiClient.ProxyRequestAsync(outboundConfig.NyxApiKey, ...) 发到飞书
Outbound 发消息:
直接用 NyxIdApiClient.ProxyRequestAsync()
不走 channel registration, 不走 ChannelUserGAgent
API Key 永不过期, 无 token 刷新问题
Cron 触发:
引入 Cronos NuGet 包 (当前 repo 无此依赖, 需新增)
CronScheduleCalculator: cron expression → 下一次触发时间 (one-shot)
GAgentBase.ScheduleSelfDurableTimeoutAsync() one-shot (已有基础设施)
Orleans Reminders 跨 pod 持久化 (已有)
每次 fire 后 Cronos 重新算下一次触发时间, 重挂 one-shot timeout
不用固定 period (无法正确表达时区/DST)
2. OutboundConfig (agent 发消息的凭证)
不是 channel registration。一个飞书 bot 只有一个 registration (管家用的)。
agent 自己持有 outbound 配置:
OutboundConfig {
ConversationId // 发到哪个飞书私聊/群 (从创建对话上下文获取)
NyxProviderSlug // api-lark-bot (复用 bot 的 NyxID provider)
NyxApiKey // nyxid_api_keys 创建的永不过期 key (scope=proxy)
OwnerNyxUserId // 谁拥有这个 agent (NyxID user id)
}
agent 发消息时:
NyxIdApiClient.ProxyRequestAsync(
outboundConfig.NyxApiKey, // API Key 认证
outboundConfig.NyxProviderSlug, // "api-lark-bot"
"open-apis/im/v1/messages?receive_id_type=chat_id",
"POST",
body, // { receive_id, msg_type, content }
ct)
3. Aevatar-native Daily Report Skill
/daily SKILL.md 的 Aevatar 适配版。不是直接复用 chrono-ai-ceo 的 skill(依赖 bash/gh CLI),改写为 NyxIdProxyTool 调用:
原: gh api "search/commits?q=author:{username}+author-date:>={7d_ago}"
改: NyxIdProxyTool → GET api-github/search/commits?q=author:{username}+author-date:>={7d_ago}
原: npx @larksuite/cli api POST /open-apis/attendance/v1/user_approvals/query
改: NyxIdProxyTool → POST api-feishu-bot/open-apis/attendance/v1/user_approvals/query
原: gh project item-list 3 --owner ChronoAIProject --format json
改: NyxIdProxyTool → GET api-github/graphql (Projects v2 API)
模板 1 hard gate 的前置任务。
4. IHumanInteractionPort
位置: src/Aevatar.Foundation.Abstractions/HumanInteraction/
接口:
IHumanInteractionPort
DeliverSuspensionAsync(HumanInteractionRequest, string deliveryTargetId, CancellationToken)
模型:
HumanInteractionRequest
ActorId (run actor id, 用于 /api/workflows/resume)
RunId, StepId
SuspensionType ("approval" | "input")
Prompt, Content (待确认内容)
Options (["approve", "reject", "edit"])
TimeoutSeconds, Metadata
deliveryTargetId 解析链:
deliveryTargetId = AgentRegistryEntry.AgentId
FeishuCardAdapter 内部:
1. 查 AgentRegistryGAgent readmodel, 拿到 AgentRegistryEntry
2. 从 entry 取 ConversationId, NyxProviderSlug, ApiKeyId
3. 从 ApiKeyId 解析实际 key (或 entry 直接存 encrypted key ref)
4. NyxIdApiClient.ProxyRequestAsync(apiKey, slug, ...) 发卡片
传输细节全部在 adapter 内部, Foundation 层只看到 deliveryTargetId
完整链路:
WorkflowRunGAgent
→ HumanApprovalModule 发布 WorkflowSuspendedEvent
→ Projection/Observation 层捕获
→ IHumanInteractionPort.DeliverSuspensionAsync(request, agentId)
→ FeishuCardAdapter 查 registry → 拿到凭证 → 构建卡片 JSON → 发飞书
→ 用户点击按钮
→ 飞书 card.action.trigger → Aevatar ChannelCallbackEndpoints
→ action 有 run_id+step_id → DispatchWorkflowResumeAsync() → /api/workflows/resume
Adapter:
FeishuCardAdapter (Day One) — 依赖 AgentRegistryGAgent readmodel 解析凭证
AGUIAdapter (已有, HTTP/SSE)
未来: OpenUI, Telegram
Timeout: 默认 reject (不执行操作)
5. AgentBuilderTool
位置: src/Aevatar.AI.ToolProviders.NyxId/Tools/AgentBuilderTool.cs (或独立项目)
模式: action-based dispatch
Actions:
list_templates → 返回可用模板
create_agent → 见下方流程
list_agents → 按 scope + owner 查询
agent_status → last_run_time, next_scheduled, error_count, state
delete_agent:
1. CancelDurableCallbackAsync() — 取消 Orleans Reminder
2. nyxid_api_keys action=delete key_id={apiKeyId} — revoke 永久 API Key
3. AgentRegistryGAgent 标记 tombstone (软删除)
4. Day One 不清理 workflow definition (IScopeWorkflowCommandPort 无 delete 语义)
Post-Day One: 新增 workflow deactivation port
5. 返回确认卡片
create_agent 内部流程:
1. 检查 ChatType == "p2p" (群聊拒绝, 提示私聊)
2. nyxid_api_keys action=create (non-expiring, scope=proxy, allowed_services)
用当前用户的 session token 创建 (用户在线时完成, 不能推迟)
3. 构建 OutboundConfig (conversationId 从当前对话上下文获取)
4. 创建 SkillRunnerGAgent 或 WorkflowGAgent, 把 OutboundConfig 存入 state
5. 注册 Orleans Reminder (cron → timer)
6. 返回 agent ID + 状态卡片
不复用 channel_registrations (语义不对, 且强制取 session token)
不创建新的 channel bot registration (一个 bot 一个 registration)
6. AgentRegistryGAgent (agent 元数据 fact owner)
位置: agents/ 或 src/platform/
长期 actor, 持有所有用户创建的 agent 的元数据。
list_agents / agent_status / delete_agent 都查这个 registry。
IScopeWorkflowQueryPort 只覆盖 workflow summary, 不覆盖 SkillRunnerGAgent。
通用 actor store 没有 owner/schedule/error 等业务字段。
AgentRegistryEntry {
AgentId, AgentType (skill_runner / workflow)
OwnerId (NyxID user id)
ScopeId
TemplateName
ScheduleCron
OutboundConversationId
ApiKeyId (用于 delete 时 revoke)
CreatedAt
Status (running / paused / error)
LastRunTime, NextScheduledRun, ErrorCount
}
更新机制:
SkillRunnerGAgent 执行完成 → AgentExecutionCompletedEvent
→ registry projection 更新 LastRunTime / ErrorCount
→ agent_status 查 registry readmodel
6. SchedulerGAgent (模板 2 定时触发)
WorkflowGAgent 只承载 definition facts, 不持有 timer。
SchedulerGAgent 持有 Orleans Reminder:
- 绑定: definition actor id + workflow name + cron
- timer fire → 创建新 WorkflowRunGAgent (run-scoped) 执行一次
- WorkflowRunGAgent 执行完成即结束
或者: 管家自身持有 timer (管家是长期 actor), 省去新 GAgent
48h Token 续期方案
问题: timer 触发的 actor 没有请求上下文, 无法获取 NyxID session token。channel_registrations 的 update_token 只是"写入当前调用者的 token",不能凭空生成新 token。
方案: agent 自持 NyxID API Key (永不过期), 存在 OutboundConfig 中。
已验证全链路:
- NyxID
POST /api/v1/api-keys 支持 expires_at 省略 (永不过期)
- NyxID proxy auth middleware 同时支持 JWT 和 API Key (同一个 Bearer 格式)
- API Key 关联
user_id 自动传给下游, 用于查找用户的 credentials
- Aevatar 已有
nyxid_api_keys tool (create/list/rotate/delete)
流程:
管家创建 agent 时 (用户在线, 有 session token):
1. nyxid_api_keys action=create
name: "aevatar-agent-{agent_id}"
scopes: "proxy"
allowed_service_ids: [api-lark-bot service id, api-github service id]
expires_at: 省略 (永不过期)
2. 拿到 full_key (创建时一次性返回)
3. 存入 SkillRunnerGAgent.State.OutboundConfig.NyxApiKey
4. agent timer 触发 → 用 API Key 发 outbound → 永不过期
不涉及 channel_registrations,不需要改 ChannelRegistrationTool。
card.action.trigger 实现 (Aevatar 侧)
Inbound: LarkPlatformAdapter 扩展
LarkPlatformAdapter.ParseInboundAsync() 当前只处理 im.message.receive_v1 (第 90 行)。需扩展:
// 新增 card.action.trigger 分支:
if (eventType == "card.action.trigger")
{
// event.operator.open_id → senderId
// event.context.open_chat_id → conversationId
// event.context.open_message_id → messageId
// event.action.value + event.action.form_value → action 数据
return new InboundMessage {
Platform = Platform,
ConversationId = chatId,
SenderId = operatorId,
Text = actionJsonSerialized,
MessageId = messageId,
ChatType = "card_action",
};
}
Routing: ChannelCallbackEndpoints 分流
// ParseInboundAsync 之后, DispatchToUserActorAsync 之前:
if (inbound.ChatType == "card_action")
{
var action = JsonDocument.Parse(inbound.Text);
if (action.RootElement.TryGetProperty("run_id", out _) &&
action.RootElement.TryGetProperty("step_id", out _))
{
// 确定性路由: workflow resume, 不过 LLM
await DispatchWorkflowResumeAsync(action, actorRuntime);
return Results.Ok(new { status = "resume_dispatched" });
}
}
// 其他 card_action (select_template 等) + 普通消息 → 正常 chat 路径
await DispatchToUserActorAsync(inbound, registration, actorRuntime, cache);
Outbound: LarkPlatformAdapter 卡片发送
SendReplyAsync() 当前硬编码 msg_type: "text" (第 179 行)。需扩展:
string msgType = IsFeishuCardJson(replyText) ? "interactive" : "text";
string content = msgType == "interactive"
? replyText
: JsonSerializer.Serialize(new { text = replyText });
飞书后台配置 (运维)
在 bot 应用"事件订阅"中添加 card.action.trigger 事件类型 (走同一个 webhook URL)。这不是代码任务,但是发布前置条件。
飞书卡片设计
5 种卡片类型 (Feishu Card JSON 2.0):
| 卡片 |
触发时机 |
关键组件 |
| 欢迎+模板选择 |
用户首次私聊 |
button (日报/社交) |
| 参数收集表单 |
选择模板后 |
form: input(repos) + picker_time + select_static(timezone) |
| OAuth 授权 |
GitHub 未授权 |
button with multi_url (跳转浏览器, 3s 内返回卡片) |
| 创建成功 |
agent 创建完成 |
markdown(配置摘要) + button(测试运行/查看状态) |
| 社交内容确认 |
LLM 生成内容后 (HumanApproval) |
markdown(预览) + button(确认/拒绝/编辑, value 含 run_id+step_id) |
LLM Provider
Org 预配置, 用户零配置。NyxID org-shared credentials + LLM Gateway。
Scope 映射
Day One: 共享预创建 scope, 用 scope + owner NyxID user id 做 namespace。
用户身份
- 私聊: per-user NyxID OAuth → ChannelUserGAgent.State.NyxidAccessToken (已有)
- agent 的 API Key 属于该用户 → agent 拉该用户的 GitHub/Lark 数据
- 群聊: Day One 不开放 agent 创建 (安全风险: 个人 credential 不应群共享)
- Post-Day One: 群聊用 NyxID org credential
已有基础设施 (无需新建)
| 组件 |
位置 |
用途 |
| ChannelCallbackEndpoints |
agents/ChannelRuntime/ |
飞书 webhook 直入 (需扩展 card_action 分流) |
| ChannelUserGAgent |
agents/ChannelRuntime/ |
per-sender 会话管理 + NyxID 身份绑定 |
| LarkPlatformAdapter |
agents/ChannelRuntime/Adapters/ |
Lark 消息解析 (需扩展 card + outbound card) |
| AIGAgentBase |
src/Aevatar.AI.Core/ |
SkillRunnerGAgent 基类 |
| ScheduleSelfDurableTimerAsync |
src/Foundation.Core/GAgentBase.cs |
定时触发 (timer API 已有, cron 解析需新增) |
| RuntimeCallbackSchedulerGrain |
src/Foundation.Runtime.Orleans/ |
Orleans Reminders 实现 |
| HumanApprovalModule |
src/workflow/Workflow.Core/Modules/ |
审批事件流 |
| WorkflowSuspendedEvent/ResumedEvent |
src/workflow/ |
暂停/恢复协议 |
| IScopeWorkflowCommandPort |
src/platform/ |
Workflow 创建 |
| NyxIdApiClient.ProxyRequestAsync |
src/AI.ToolProviders.NyxId/ |
outbound API 调用 |
| nyxid_providers |
NyxidChat tools |
OAuth 连接 |
| nyxid_api_keys |
NyxidChat tools |
API Key 创建 (non-expiring) |
| nyxid_proxy |
NyxidChat tools |
API 代理 |
| UseSkillTool |
src/AI.ToolProviders.Skills/ |
Skill 加载 |
NyxID proxy service seeds (已配置, 无需新建):
需要新建/扩展
| 工作项 |
类型 |
估算 |
| SkillRunnerGAgent + OutboundConfig |
新 GAgent 类型 |
2-3 天 |
| Aevatar-native daily skill |
改写 /daily 为 NyxIdProxyTool 调用 |
1-2 天 |
| AgentBuilderTool |
新 IAgentTool (不复用 channel_registrations) |
1-2 天 |
| AgentRegistryGAgent + readmodel projection |
新 actor (agent 元数据 fact owner) |
1-2 天 |
| IHumanInteractionPort + FeishuCardAdapter |
新抽象 + adapter |
2-3 天 |
| ChannelCallbackEndpoints card_action 分流 |
扩展 endpoint (run_id 检查 → resume) |
0.5 天 |
| LarkPlatformAdapter card.action.trigger 解析 |
扩展 adapter inbound |
0.5 天 |
| LarkPlatformAdapter card 发送 |
扩展 adapter outbound |
0.5 天 |
| Cronos NuGet + CronScheduleCalculator |
新依赖 + one-shot 循环 (repo 当前无 Cronos) |
0.5 天 |
| SchedulerGAgent (模板 2 定时) |
新 GAgent 或管家持有 |
0.5 天 |
| 飞书后台 card.action.trigger 事件订阅 |
运维配置 |
0.5h |
| NyxID org + LLM credentials |
运维配置 |
0.5h |
NyxID#306 outbound 卡片发送 |
NyxID lark.rs (Kaiwei) |
✅ 已部署 (PR #314) |
并行化策略
Lane A: SkillRunnerGAgent + OutboundConfig → daily skill 改写 → AgentBuilderTool
Lane B: IHumanInteractionPort → FeishuCardAdapter
Lane C: ChannelCallbackEndpoints 分流 + LarkPlatformAdapter 扩展 (card inbound + outbound)
Lane D: ~~NyxID#306~~ ✅ 已部署
A + B + C + D 并行开发。合并后集成测试。
Timeline
| 日期 |
Gate |
验收标准 |
| 04-16 |
执行计划 + 接口定义 |
IHumanInteractionPort 接口 + SkillRunnerGAgent 骨架 + OutboundConfig proto |
| 04-18 |
A0 Done |
测试网稳定 |
| 04-19 |
卡片决策点 |
card.action.trigger 分流 + LarkAdapter card 发送就绪, 否则 fallback 纯文本 |
| 04-21 |
模板 1 端到端 |
SkillRunner 执行 daily skill → 输出在测试飞书私聊 → 存活 pod 重启 |
| 04-23 |
模板 2 端到端 |
LLM 生成 → 确认卡片 → 确认后发 X |
| 04-25 |
Day One |
≥5 agent 运行 48h+, ≥3 用户自主创建 (私聊) |
协作
Review Status
| Review |
Status |
关键发现 |
| /office-hours |
APPROVED |
方案 C (卡片 UX) + tool-based concierge |
| /plan-ceo-review |
CLEAR |
2 模板 + SkillRunnerGAgent + IHumanInteractionPort |
| /plan-eng-review |
CLEAR |
新 GAgent 类型, port 渲染+发送, Cronos 库 |
| /plan-design-review |
CLEAR (8/10) |
表单流, 5 种卡片, 飞书 JSON 2.0 |
| Review 报告 R1 |
7/7 CONFIRMED |
消息路径, timer 归属, skill 改写, tool 清单, scope, token |
| Review 报告 R2 |
3/3 收口 |
card_action 路由 owner, API Key 不走 registration, outbound 模型 |
| Review 报告 R3 |
4/4 收口 |
AgentRegistry readmodel, Port 不泄漏传输细节, cron one-shot 循环, delete 完整清理 |
背景
Day One (04-25) 目标:外部用户在飞书私聊 bot 创建持久运行的自动化 agent。成功标准:≥5 agent 运行 48h+,≥3 用户无人工指导完成创建。
关联:#182 (Day One spec)、#183 (Owner 自主目标)、NyxID#306 (飞书卡片 outbound 发送 — ✅ 已部署 PR #314)
核心约束
架构方案(收敛版,含全部 review 修正 + 第三轮收口)
架构图
关键架构决策
新增组件
1. SkillRunnerGAgent
2. OutboundConfig (agent 发消息的凭证)
3. Aevatar-native Daily Report Skill
/daily SKILL.md 的 Aevatar 适配版。不是直接复用 chrono-ai-ceo 的 skill(依赖 bash/gh CLI),改写为 NyxIdProxyTool 调用:
模板 1 hard gate 的前置任务。
4. IHumanInteractionPort
5. AgentBuilderTool
6. AgentRegistryGAgent (agent 元数据 fact owner)
6. SchedulerGAgent (模板 2 定时触发)
48h Token 续期方案
问题: timer 触发的 actor 没有请求上下文, 无法获取 NyxID session token。channel_registrations 的 update_token 只是"写入当前调用者的 token",不能凭空生成新 token。
方案: agent 自持 NyxID API Key (永不过期), 存在 OutboundConfig 中。
已验证全链路:
POST /api/v1/api-keys支持expires_at省略 (永不过期)user_id自动传给下游, 用于查找用户的 credentialsnyxid_api_keystool (create/list/rotate/delete)流程:
不涉及 channel_registrations,不需要改 ChannelRegistrationTool。
card.action.trigger 实现 (Aevatar 侧)
Inbound: LarkPlatformAdapter 扩展
LarkPlatformAdapter.ParseInboundAsync()当前只处理im.message.receive_v1(第 90 行)。需扩展:Routing: ChannelCallbackEndpoints 分流
Outbound: LarkPlatformAdapter 卡片发送
SendReplyAsync()当前硬编码msg_type: "text"(第 179 行)。需扩展:飞书后台配置 (运维)
在 bot 应用"事件订阅"中添加
card.action.trigger事件类型 (走同一个 webhook URL)。这不是代码任务,但是发布前置条件。飞书卡片设计
5 种卡片类型 (Feishu Card JSON 2.0):
LLM Provider
Org 预配置, 用户零配置。NyxID org-shared credentials + LLM Gateway。
Scope 映射
Day One: 共享预创建 scope, 用 scope + owner NyxID user id 做 namespace。
用户身份
已有基础设施 (无需新建)
NyxID proxy service seeds (已配置, 无需新建):
api-github(https://api.github.com, bearer)api-lark-bot(https://open.feishu.cn, transparent tenant token exchange)api-twitter(https://api.x.com/2, bearer)需要新建/扩展
NyxID#306 outbound 卡片发送NyxID lark.rs (Kaiwei)并行化策略
Timeline
协作
NyxID outbound 卡片发送✅ 已部署 (PR Fix hosted service registration ambiguity in channel runtime #314). 卡片发送通过 metadata.card key, card-only reply (text=null) 在 lark/feishu 上合法Review Status