agent-context-contract 0.2.2

Transport-neutral contracts for Agent Infra context APIs
Documentation
# Agent Context HTTP API

`agent-context-infra` 是 Conversation/Context 的数据所有者,也是外部 Knowledge API 的防腐层。它持久化会话事实、模型真实可见的 Context Snapshot、压缩/组装推导关系、ModelInvocation 和向量投影;Knowledge 数据仍归外部 provider 所有,服务内部不实现知识摄取或知识库。

| Item | Value |
| --- | --- |
| Binary | `agent-context` |
| Default bind | `127.0.0.1:5120` |
| Service / audience | `agent-context-infra` |
| Context prefix | `/internal/v1/context` |
| Knowledge prefix | `/internal/v1/knowledge` |
| Public Gateway prefixes | `/v1/context`、`/v1/knowledge` |

## Authentication and tenant isolation

生产流量可以使用经 Gateway 验证的用户身份,也可以直接使用 `agent-runtime-identity` 颁发的 audience-bound 短期 Runtime Identity credential。Gateway 不持有 bootstrap credential,也不会把用户 token 隐式兑换成 Runtime Identity。需要最终用户上下文的 Knowledge 调用仍通过受保护的 `x-infra-user-authorization` 显式携带用户凭证。

Context 校验 runtime token 的签名、issuer、audience、expiry、capability、tenant/project/subject scope 和用户 token hash。所有持久化读写均使用 credential scope 隔离,不能依赖请求 JSON 中的 user/tenant 字段做授权。Knowledge 转发继续携带这两种凭证。

本地开发可通过 `AGENT_CONTEXT_SERVICE_TOKEN` 使用静态 Bearer token;未配置且未启用 runtime-token verifier 时为 open mode。`GET /health` 始终不鉴权。客户端提交的 `x-infra-*` 身份头只能由 Gateway/mesh 信任边界生成。

## Error shape

错误响应包含兼容字段和统一 envelope:

```json
{
  "status": 403,
  "message": "the runtime token lacks the required capability",
  "error": {
    "code": "INTERNAL_TOKEN_FORBIDDEN",
    "message": "the runtime token lacks the required capability",
    "category": "auth",
    "retryable": false,
    "requestId": "request-id",
    "details": {}
  }
}
```

| HTTP | Typical cause |
| ---: | --- |
| 400 | validation failure, invalid cursor, oversized collection |
| 401 | missing/invalid/expired runtime token or invalid token binding |
| 403 | capability or scope forbidden |
| 404 | Snapshot、ModelInvocation、Context entry 或 operation 不存在 |
| 409 | conflicting immutable record or idempotency conflict |
| 413 | request exceeds bounded payload limits |
| 502 | external Knowledge provider returned an invalid/upstream error |
| 503 | required Knowledge provider unavailable |

## Health

`GET /health` 不鉴权,成功返回:

```json
{"status":"ok","service":"agent-context-infra"}
```

## Endpoint matrix

### Conversation facts

| Method and path | Capability | Purpose |
| --- | --- | --- |
| `POST /internal/v1/context/messages/batch` | `messages:append` | 幂等追加消息;相同 conversation 内按 ID 去重 |
| `POST /internal/v1/context/messages/recent` | `messages:read` | 读取最近消息,返回顺序为 oldest-first |
| `POST /internal/v1/context/messages` | `messages:read` | cursor 分页读取 |
| `POST /internal/v1/context/messages/search` | `messages:search` | bounded 文本检索 |
| `POST /internal/v1/context/conversations/delete` | `messages:delete` | 发起 conversation 删除 operation |
| `POST /internal/v1/context/conversations/retention` | `messages:delete` | 执行 retention operation |
| `POST /internal/v1/context/operations` | `messages:delete` | 查询删除/维护 operation |
| `GET /internal/v1/context/maintenance/status` | `messages:read` | 查询维护状态 |

追加请求示例:

```json
{
  "messages": [{
    "id": "message-1",
    "scope": {"conversationId": "conversation-1"},
    "role": "user",
    "content": "hello",
    "sequence": 0,
    "createdAtMs": 0,
    "metadata": null
  }]
}
```

成功返回 `{"appended":1,"skipped":0}`。`sequence=0` 与 `createdAtMs=0` 由服务端补齐。读取默认 limit 50、最大 500;cursor 对 scope 和 filter 绑定,不能跨查询复用。

### Snapshot, derivation and model invocation

| Method and path | Capability | Success | Purpose |
| --- | --- | ---: | --- |
| `POST /internal/v1/context/windows` | `context:write` | 201 | 保存模型可见 Context |
| `POST /internal/v1/context/windows/get` | `context:read` | 200 | 按 ID 读取 Snapshot |
| `POST /internal/v1/context/derivations` | `context:write` | 201 | 保存 assemble/compress/trim/reorder 推导边 |
| `POST /internal/v1/context/model-invocations` | `context:write` | 200 | 幂等保存 LLM 调用审计记录 |
| `POST /internal/v1/context/model-invocations/get` | `context:read` | 200 | 按 ID 读取调用记录 |

Snapshot stage 为 `original`、`assembled` 或 `compressed`。每个 item 明确记录 ordinal、kind、content 和可选 source record;单个 Snapshot 最多 2,000 items、16 MiB。外部 Knowledge 命中使用 `knowledgeEvidence` 保存最小可回放证据,resource/revision/segment reference 对 Context 不透明。

```json
{
  "id": "snapshot-1",
  "conversation": {"conversationId": "conversation-1"},
  "stage": "assembled",
  "purpose": "chat_completion",
  "tokenCount": 320,
  "items": [{
    "id": "item-1",
    "ordinal": 0,
    "kind": "message",
    "role": "user",
    "content": "hello",
    "sourceRecordId": "message-1",
    "metadata": null
  }]
}
```

ModelInvocation 的 `requestWindowId` 必须指向真实发送给模型的 Snapshot。不可变记录用相同 ID 和相同内容重试是幂等的;相同 ID、不同内容返回 conflict。

### Context retrieval and administration

| Method and path | Capability |
| --- | --- |
| `POST /internal/v1/context/entries`、`entries/get` | `context:write`、`context:read` |
| `POST /internal/v1/context/recall` | `context:retrieve` |
| `POST /internal/v1/context/entries/merge`、`correct`、`expire` | `context:write` |
| `POST /internal/v1/context/exports`、`imports` | `context:admin` |
| `POST /internal/v1/context/export-artifacts/get`、`export-artifacts/chunk` | `context:admin` |
| `POST /internal/v1/context/projections/vector/rebuild` | `context:admin` |
| `POST /internal/v1/context/advanced-operations` | `context:admin` |

向量检索是 Context Infra 的内部投影能力,当前由 OceanBase 向量存储实现。Conversation/Context records 是 source of truth;向量索引可以重建,不能反向成为事实所有者。导出使用有界 chunk,避免把完整 conversation 一次载入内存。

## External Knowledge facade

| Method and path | Capability | Purpose |
| --- | --- | --- |
| `GET /internal/v1/knowledge/capabilities` | `knowledge:read` | provider 能力和限制 |
| `POST /internal/v1/knowledge/query` | `knowledge:query` | provider-neutral 查询 |
| `POST /internal/v1/knowledge/resolve-resources` | `knowledge:query` | 解析不透明资源引用 |

```json
{
  "query": "workspace deployment policy",
  "requirement": "required",
  "provider": "enterprise-knowledge",
  "scopeRef": "space-1",
  "topK": 5,
  "filters": {}
}
```

`requirement` 可为:

- `required`:provider 失败时请求失败,通常返回 `503`;
- `optional`:provider 失败时返回空 hits、`degraded=true` 和归一化 `degradationReason`;
- `disabled`:不调用 provider。

`topK` 最大 100,query 最大 16 KiB,单个 excerpt 最大 64 KiB。Contract 不暴露 provider SDK 类型,也不定义摄取、embedding 或知识持久化模型。

## Public Gateway mapping

Gateway 仅改写 prefix,领域 JSON contract 不变:

```text
/v1/context/*   -> /internal/v1/context/*
/v1/knowledge/* -> /internal/v1/knowledge/*
```

typed SDK 直接使用 `/internal/v1/context/*` 和 `/internal/v1/knowledge/*` contract path。连接 Gateway 时这些路径仍经过完整 Gateway policy 与双 token 签发;连接 Context 服务时则是 mesh 内直连。原始外部 HTTP client 应使用 `/v1/*`。

公开调用示例:

```bash
curl -sS https://infra.example.com/v1/knowledge/query \
  -H 'Authorization: Bearer <sky-gateway-token>' \
  -H 'Content-Type: application/json' \
  -d '{"query":"deployment policy","requirement":"optional","topK":5,"filters":{}}'
```

调用方不得提供 runtime token 或 `x-infra-user-authorization`;这两者由 Gateway 内部通信链路管理。

## Related crates

| Crate | Role |
| --- | --- |
| `agent-context-contract` | Context path constants and DTOs |
| `agent-knowledge-contract` | provider-neutral Knowledge DTOs; no storage types |
| `agent-context-runtime` | use cases, ports and Context ownership rules |
| `agent-context-adapter-sql` | SQL/OceanBase persistence and vector projection |
| `agent-knowledge-adapter-http` | bounded external Knowledge HTTP adapter |
| `agent-context-server` | Axum host, auth, capability and token-binding enforcement |