Initial Daily Campaign Agent release

This commit is contained in:
joliehill294-hue
2026-09-16 21:00:13 +08:00
commit 14fc94b3f0
86 changed files with 10766 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
# 架构与数据流
```mermaid
flowchart TB
subgraph Browser
C[assistant-ui Composer]
T[Thread + Toolkit Tool UI]
E[Event Timeline]
S[HarnessClientStore]
C --> S
S --> T
S --> E
end
subgraph Server
F[Fastify API]
R[SessionRegistry]
ES[Memory EventStore]
AB[ApprovalBroker]
AS[AttachmentStore]
MS[ModelConnectionStore]
P[Pi AgentSession]
RL[DefaultResourceLoader]
SW[SafeWorkspace]
F --> R --> P
R --> ES
R --> AB
R --> AS
R --> MS
P --> RL
P --> SW
end
S -->|JSON / multipart| F
ES -->|SSE sequence| S
```
每个 Web Session 有随机 ID、独立 cwd、SessionManager、事件 sequence、运行锁、AbortController、Approval、Attachment 和 ModelRuntime。Fastify 只解析协议、组合依赖并序列化错误;核心状态在 interface + memory implementation 中。
消息 POST 先完成模型/附件/并发校验,接受后立即返回 `202 runId`。后台 `prompt()` 的流式 Pi Event 经单一 adapter 映射为 HarnessEvent。SSE reducer 按 sequence/eventId 去重;assistant-ui External Store 只投影 reducer 结果。
Tool 先经过 Pi extension `tool_call`。只读工具做 workspace/Skill canonical path preflight;保存工具等待服务器 ApprovalBroker。Tool 未通过 preflight 时永不执行。
+13
View File
@@ -0,0 +1,13 @@
# 10–15 分钟课堂演示
1. **1 分钟**:打开三栏界面,说明 Skills / Thread / Timeline 分工。
2. **2 分钟**:打开 Settings,配置临时模型密钥,强调密钥只在 Session 内存中。
3. **1 分钟**:点击 Demo Prompt,仅填入不自动执行。
4. **2 分钟**:发送后观察 `202 runId`、`run.started` 和流式 delta。
5. **2 分钟**:解释 progressive disclosure:Catalog 没有正文,Pi 通过 read 加载 `ads-analysis`,Harness 推导 requested/loaded。
6. **2 分钟**:观察 `mock_ads_metrics` Tool UI,强调 fixture 非真实平台数据。
7. **2 分钟**:到 `save_report_draft` 暂停,展示 inline 风险状态与 modal;先演示 Deny,说明未写文件。
8. **2 分钟**:重新运行并 Approve,查看 approval.resolved、tool.completed 与最终报告。
9. **可选 2 分钟**:上传图片;切到 text-only 模型展示发送阻止与 422,不发生静默降级。
讨论题:为什么 assistant-ui 不能执行 Tool?为什么 Approval 不是 Sandbox?为什么 Skill loaded 必须由 canonical path + 成功结果推导?单进程 EventStore 如何升级为分布式?
+21
View File
@@ -0,0 +1,21 @@
# Event Mapping
| Pi / Harness 事实 | HarnessEvent | assistant-ui / UI 状态 |
|---|---|---|
| HTTP message accepted | `user.message`, `run.started` | user message, running |
| `message_update.text_delta` | `assistant.delta` | 合并同 messageId 文本 |
| assistant `message_end` | `assistant.completed` | complete/stop |
| `tool_execution_start` | `tool.requested` | tool-call waiting/running |
| successful `tool_execution_end` | `tool.completed` | result/success |
| failed `tool_execution_end` | `tool.failed` | isError/error |
| canonical Skill `read` preflight | `skill.requested` | Catalog 高亮 |
| same `read` successful `tool_result` | `skill.loaded` | loaded 高亮 |
| ApprovalBroker request | `approval.required` | Tool card + modal pending |
| broker decision/timeout/abort | `approval.resolved` | approved/denied/expired/cancelled |
| prompt resolves | `run.completed` | idle |
| user abort / deadline | `run.cancelled` | incomplete/cancel reason |
| prompt rejects | `run.failed` | error |
`skill.requested`/`skill.loaded` 是 Harness 推导事件,不是 Pi 原生事件。未识别 Pi Event 被安全忽略。高频 delta 只在内存窗口和 SSE 中保留,不包含 thinking、密钥或图片 base64。
SSE 的 `afterSequence` 优先于 `Last-Event-ID`。历史仍在窗口时补发;游标早于最早保留 sequence 时发送 `harness.reset` 控制事件,避免静默缺口。客户端再按 eventId/sequence 去重。
+11
View File
@@ -0,0 +1,11 @@
# Production Roadmap
1. **身份与租户**:OIDC、RBAC、Session ownership、服务端授权、CSRF、CORS allowlist、审计主体。
2. **持久化**:Postgres 保存 Session/Run/Approval;Redis 做运行锁、pub/sub、SSE cursor;事件采用 transactional outbox。
3. **附件**:私有对象存储、短期签名 URL、病毒扫描、内容安全、生命周期策略、租户配额。
4. **凭证**:KMS/Vault、envelope encryption、短期 token、provider-scoped secret、轮换与撤销。
5. **隔离**:每 Run 容器/VM/micro-VM、只读基础镜像、最小挂载、seccomp、资源限额、网络 egress policy。
6. **执行控制**:集中 policy engine、Tool capability token、双人审批、风险分级、不可抵赖审计。
7. **弹性**:Job queue、幂等 run ownership、分布式 abort、SSE gateway、backpressure、恢复与重放。
8. **观测**:结构化脱敏日志、metrics、trace、model/tool latency、cost、审批等待、事件 gap 告警。
9. **质量**:provider contract tests、真实 sandbox 集成、fuzz 路径/图片/SSRF、故障注入、浏览器 E2E、可访问性 CI。
+29
View File
@@ -0,0 +1,29 @@
# 安全边界
## 信任与隔离
Pi 没有内置 Sandbox;Project Trust 不是 Sandbox。Extension/Tool 与 Pi 继承 Node 进程权限。课堂版只提供应用层最小权限,不能替代容器、VM、micro-VM 或远程 sandbox。
## 凭证
密钥只在 Session `InMemoryCredentialStore` 中存在。输入/输出/log/SSE 均按 allowlist 和 redaction 处理;前端不持久化密钥。不接受任意 headers、命令型凭证解析或 query key。
## 路径
SafeWorkspace 对绝对路径、`..`、NUL、canonical root、父目录 realpath 和符号链接做检查。保存路径固定为 `report.md`,临时文件使用排他打开、`O_NOFOLLOW`,同步后原子 rename。Skill 正文例外读取只允许已发现索引中的 canonical `SKILL.md`。
## 图片
扩展名、声明 MIME、magic bytes 三重一致;只收 JPEG/PNG/WebP;限制字节与总像素;sharp 自动旋转并重新编码,移除 EXIF/ICC;随机服务端文件名;附件只能通过 `(sessionId, attachmentId)` 查找。
## 表格附件
只接收 XLSX、UTF-8 CSV 和 UTF-8 TSV;默认限制 20 MB。XLSX 校验 ZIP 签名和工作簿关键条目,CSV/TSV 拒绝 NUL 与非法 UTF-8。原始文件使用随机服务端文件名写入当前 Session,模型只能把当前 Session 的 `attachmentId` 交给固定的 `analyze_uploaded_report` 工具;工具以无 shell 的参数数组执行仓库内置分析器。
## SSRF
Base URL 拒绝非 HTTPS(开发 loopback 除外)、userinfo、fragment、凭证参数、畸形 URL、私网/loopback/link-local/metadata 解析结果。生产应再配置 egress proxy,在每次重定向/连接前做等价检查;本课堂实现不主动跟随连接测试重定向。
## 审批
审批发生在 Pi `tool_call`、Tool 执行之前。Broker 决策是唯一权威;超时等同拒绝;Abort/删除立刻取消;重复同决定幂等、冲突决定 409。审批不替代路径和 schema 校验。