forked from EduCraft/curriculum-project-hub
f8c3e16a52
A-4 FeishuEventReceipt: - 新增 FeishuEventReceipt 表(eventId @unique),lark ws 至少一次投递的幂等兜底 - MessageReceiveEvent 加 header.event_id 字段 - trigger 入口查重:已存在的 event_id → 跳过(不建第二次 run) - resetDb 加入 FeishuEventReceipt A-8 审计写入: - AuditEntry 加 projectId(权限拒绝/slash 命令等无 run 的审计点可定位) - 新建 src/audit.ts:writeAudit(prisma, entry) best-effort 写入(吞错,不阻断 trigger) - trigger 五个审计点:trigger.denied / trigger.role_denied / run.created / run.lock_race / run.finished / run.failed - 新增 audit unit 测试(3) + trigger 集成测试(去重 + 审计,2) 59 tests 全过。
51 lines
2.3 KiB
Markdown
51 lines
2.3 KiB
Markdown
# ADR 0017: AgentSession Is Provider-Bound
|
|
|
|
## Status
|
|
|
|
Accepted.
|
|
|
|
## Context
|
|
|
|
ADR-0002 says a long-lived `AgentSession` can be reused by many runs, but never
|
|
addressed the provider/model dimension. The agent layer is provider-agnostic
|
|
(ADR-0001..0003 never required Claude specifically; the `@Claude` trigger name
|
|
is a product brand, not a provider commitment). In practice the Hub routes
|
|
model calls through an OpenAI-compatible client (e.g. OpenRouter), so a run may
|
|
target different models — and the motivation for multi-model is **role-based
|
|
routing**: drafting, reviewing, and triage suit different models.
|
|
|
|
That raises the question ADR-0002 left open: when a teacher switches model
|
|
mid-project, does the `AgentSession` carry live context across the switch, or
|
|
is it bound to a single provider/model?
|
|
|
|
## Decision
|
|
|
|
An `AgentSession` is **provider/model-bound**. A model or provider switch
|
|
starts a new `AgentSession`. The `AgentSession` does not carry live context
|
|
across a switch.
|
|
|
|
Cross-session continuity is not the session's job — it is carried by ADR-0003's
|
|
project memory and anchors, which the Hub reads on demand when seeding a new
|
|
run. This keeps B consistent with ADR-0003 ("the Hub avoids becoming a full
|
|
chat history store"): session continuity and history continuity are the same
|
|
mechanism, both via memory/anchors, never via a live cross-provider session.
|
|
|
|
Per-run model selection (the run carries its model) is the switching point.
|
|
|
|
## Consequences
|
|
|
|
- Switching model mid-project = new session; the new run seeds from project
|
|
memory/anchors (ADR-0003), not the prior session's live context.
|
|
- The agent layer is provider-agnostic: model calls go through an
|
|
OpenAI-compatible client, and the tool-calling loop is delegated to the
|
|
Vercel AI SDK (`generateText` + `tool`). The provider seam is the SDK's
|
|
`LanguageModel` interface, not a hand-rolled loop; swapping provider is
|
|
swapping the `createOpenAICompatible` factory, not rewriting the loop. No
|
|
hard dependency on a single-vendor agent SDK.
|
|
- Role-based model routing (different run kinds default to different models) is
|
|
a product/admin configuration concern, not a spec invariant.
|
|
- "AgentSession reused by many runs" (ADR-0002) holds *within* one
|
|
provider/model; it does not span a switch.
|
|
- The `@Claude` trigger name remains the product mention brand regardless of the
|
|
backing model.
|