Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/agents-api/sessions-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ On `openai_hosted` and `self_hosted` Sessions, messages sent while a Turn runs j

### Input errors

Checks run in this order: request validation, Session lookup, retry lookup, the Environment file-write gate for batches with a message, the pending-input gate, then each event in batch order. A rejected batch writes nothing and leaves any pending action unchanged. Every 409 has type `conflict_error` ([error envelope](./wire-semantics.md)).
Checks run in this order: [request validation](./wire-semantics.md#configuration-validation), whose paths start with `events[i]`, Session lookup, retry lookup, the Environment file-write gate for batches with a message, the pending-input gate, then each event in batch order. A rejected batch writes nothing and leaves any pending action unchanged. Every 409 has type `conflict_error` ([error envelope](./wire-semantics.md)).

| Case | Status and code | Message |
| --- | --- | --- |
Expand Down
4 changes: 2 additions & 2 deletions contracts/agents-api/vaults.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ A malformed, missing or foreign ID returns 404 `not_found_error`, and so does a

| Field | Rules |
| --- | --- |
| `name` | Optional. Omitted stays `null`; explicit `null` is rejected. A string is trimmed and must then hold 1–256 UTF-8 bytes |
| `name` | Optional. Omitted stays `null`; explicit `null` is rejected. A string is trimmed and must then hold 1–256 UTF-8 bytes (param `name`) |
| `metadata` | Omitted or `null` becomes `{}`. Values must be strings; another type returns 400 `invalid_request_error` with param `metadata.<key>`. The encoded object is limited to 64 KiB, with no pair-count or length limits |

A Vault reads as `id`, `object: "vault"`, `created_at`, `name` and `metadata`. There is no update route.
Expand All @@ -67,7 +67,7 @@ Deleting a Vault removes it and all its Credentials in one transaction, whatever

## Credentials

Creation takes a required `name` (trimmed, 1–256 UTF-8 bytes) and an `auth` object whose `type` is `static_bearer` or `mcp_oauth`. `mcp_server_url` must be an absolute HTTPS URL without userinfo or fragment; Core keeps it byte for byte, including any query, and performs no DNS or HTTP request.
Creation takes a required `name` (trimmed, 1–256 UTF-8 bytes) and an `auth` object whose `type` is `static_bearer` or `mcp_oauth`. `mcp_server_url` must be an absolute HTTPS URL without userinfo or fragment; Core keeps it byte for byte, including any query, and performs no DNS or HTTP request. Field errors name the field as param, such as `auth.type`, `auth.access_token` or `auth.mcp_server_url`, and an unknown or missing member has the official `Unknown parameter` or `Missing required parameter` message ([request validation](./wire-semantics.md#configuration-validation)).

A Credential reads as `id`, `vault_id`, `name`, `object: "vault.credential"`, `created_at`, `updated_at` and `auth`. `auth` holds `type` and `mcp_server_url`; an OAuth Credential adds `expires_at` and `refresh` with `client_id`, `token_endpoint`, `token_endpoint_auth.type`, `resource` and `scope`. Reads and lists need no storage key.

Expand Down
2 changes: 2 additions & 0 deletions contracts/agents-api/wire-semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ Agent create and update bodies and the inline `agent` of Session create are chec
| Function `parameters` with a string root `type` other than `object` | null | `Invalid schema for function '<name>': schema must be a JSON Schema of 'type: "object"', got 'type: "<type>"'.` |
| `text.format` JSON schema with a string root `type` other than `object` | null | `agent.text.format.schema must have top-level type "object"; got "<type>"`, also on Agent requests |

Session create and update, Vault create, Credential create and update, Environment Template create and update and Session events are checked the same way against their pinned bodies, before their own decoding. The param is the JSON path, such as `metadata`, `input[0].role`, `input[0].content[0].type`, `events[0].input[0].role`, `auth.type`, `auth.access_token`, `env.KEY` or `name`. `input` and a function result `output` take a string or an array, and any other type reports `expected a string or an array`. The Session `agent` and `environment` and the Template `network` keep their own parsers and are first checked to be objects. Rules the pinned schema does not express report the field as param with a message that names the rule: Vault and Credential `name` (1–256 UTF-8 bytes after trimming), Template `name` (1–256 characters), a nonempty `auth.token` and `auth.access_token`, an HTTPS `auth.mcp_server_url` and `auth.refresh.token_endpoint`, an RFC 3339 `auth.expires_at`, environment variable names (`env.<name>`) and a Credential update with nothing to change (param `auth`).

Within one object Core reports a union's `type` first, then unknown members, then member values in document order, then missing members in the pinned schema's order; tools before `text`, and the whole object before the duplicate and schema-root checks. Schemas without a string root `type` are not checked. Function and output schemas, `request_metadata` and `x_agents_core` keep their own parsers. Update bodies and the inline Session agent are validated before the Agent lookup, so owned, foreign, missing and malformed Agent IDs give the same response.

Core saves values the pinned shapes allow even when the selected Harness cannot run them: function names of any length and the reasoning, service tier, web search and programmatic tool calling values a Harness does not declare.
Expand Down
4 changes: 2 additions & 2 deletions contracts/agents-api/zh/sessions-events.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Session、事件和历史"
source: contracts/agents-api/sessions-events.md
source_hash: e8dfd2f21bdcf16832c60fb9fac2df42bc96553d7f1294dc2742c3eb455772a1
source_hash: 39fe5deab865e778bd8d3fa50a5d66707e10df4652b868e47f17126d962291eb
---

本契约涵盖 Session 内部发生的事情:发送输入、实时事件流,以及读取 Turn、Item 和使用量的持久化历史。Session 资源本身(创建配置、重试标识、更新、列出和删除)见 [API 行为](wire-semantics.md)。消息和函数结果内容见[消息内容](message-content.md)。[Agents API 指南](../../../docs/zh/api/public-agent-api.md)展示了使用 SDK 和 HTTP 的调用方式。
Expand Down Expand Up @@ -63,7 +63,7 @@ Turn 失败后 Session 仍可使用:新输入会启动一个新 Turn。后来

### 输入错误 {#input-errors}

检查按以下顺序执行:请求验证、Session 查找、重试查找、针对包含消息的批次的 Environment 文件写入门控、待处理输入门控,最后按批次顺序检查每个事件。被拒绝的批次不会写入任何内容,也不会改变任何待处理操作。所有 409 响应的 `type` 都是 `conflict_error`([错误封装](wire-semantics.md))。
检查按以下顺序执行:[请求验证](wire-semantics.md#configuration-validation)(路径以 `events[i]` 开头)、Session 查找、重试查找、针对包含消息的批次的 Environment 文件写入门控、待处理输入门控,最后按批次顺序检查每个事件。被拒绝的批次不会写入任何内容,也不会改变任何待处理操作。所有 409 响应的 `type` 都是 `conflict_error`([错误封装](wire-semantics.md))。

| 情况 | 状态和代码 | 消息 |
| --- | --- | --- |
Expand Down
6 changes: 3 additions & 3 deletions contracts/agents-api/zh/vaults.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Vault 与 Credential"
source: contracts/agents-api/vaults.md
source_hash: b27509949dca26f064cda765ea97229e3325dee3ff650b17fa332a756409cf34
source_hash: 1a57ba70173f8a000b6b0cb74eb7fa6a6c688b4f71d7e617b4fdfb60e6d6fef5
---

Vault 是项目所有的 Credential 容器。Credential 保存一个 HTTPS MCP server 的秘密:`static_bearer` token 或 `mcp_oauth` grant。Session 在 `vault_ids` 中关联 Vault;Core 在创建 Session 时为每个 HTTP MCP server 选择一个 Credential,只在分派工作时将解密 token 交给 Runtime。秘密只能写入:任何读取都不返回 token、refresh token、client secret 或密文。
Expand Down Expand Up @@ -60,7 +60,7 @@ MCP tool 可以通过 `credential_id` 指定 Credential;未指定时,Core

| 字段 | 规则 |
| --- | --- |
| `name` | 可选;省略保持 `null`,显式 `null` 被拒绝。字符串去除首尾空白后必须为 1–256 UTF-8 字节 |
| `name` | 可选;省略保持 `null`,显式 `null` 被拒绝。字符串去除首尾空白后必须为 1–256 UTF-8 字节(param 为 `name`) |
| `metadata` | 省略或 `null` 转为 `{}`。值必须为字符串;其他类型返回 400 `invalid_request_error`,param 为 `metadata.<key>`。编码后对象上限 64 KiB,无键值对数量或长度限制 |

Vault 读取包含 `id`、`object: "vault"`、`created_at`、`name` 和 `metadata`。没有更新路由。
Expand All @@ -69,7 +69,7 @@ Vault 读取包含 `id`、`object: "vault"`、`created_at`、`name` 和 `metadat

## Credential {#credentials}

创建需要 `name`(去除首尾空白后 1–256 UTF-8 字节)和 `auth` 对象,`type` 为 `static_bearer` 或 `mcp_oauth`。`mcp_server_url` 必须是无 userinfo、无 fragment 的绝对 HTTPS URL;Core 逐字节保留,包括 query,不执行 DNS 或 HTTP 请求。
创建需要 `name`(去除首尾空白后 1–256 UTF-8 字节)和 `auth` 对象,`type` 为 `static_bearer` 或 `mcp_oauth`。`mcp_server_url` 必须是无 userinfo、无 fragment 的绝对 HTTPS URL;Core 逐字节保留,包括 query,不执行 DNS 或 HTTP 请求。字段错误以该字段作为 param,例如 `auth.type`、`auth.access_token` 或 `auth.mcp_server_url`;未知成员或缺失成员使用官方的 `Unknown parameter` 或 `Missing required parameter` 消息([请求验证](wire-semantics.md#configuration-validation))。

Credential 读取包含 `id`、`vault_id`、`name`、`object: "vault.credential"`、`created_at`、`updated_at` 和 `auth`。`auth` 包含 `type`、`mcp_server_url`;OAuth Credential 还包含 `expires_at` 和 `refresh`,后者包含 `client_id`、`token_endpoint`、`token_endpoint_auth.type`、`resource` 和 `scope`。读取和列表无需 storage key。

Expand Down
4 changes: 3 additions & 1 deletion contracts/agents-api/zh/wire-semantics.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "API 行为"
source: contracts/agents-api/wire-semantics.md
source_hash: 851e09252847f11040524f329419587687e81d361e4e92c0a3e912d2fd887d45
source_hash: 6f57c50e736d1837b3609209812438063cc96c24c145ffe9302a93d5f9c7746f
---

已锁定版本的 OpenAI Python SDK([upstream.json](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/contracts/agents-api/upstream.json))定义了 `/v1` 路由、字段和类型。本页面说明这些类型未作规定之处 Core 的行为,例如状态码、错误字段、默认值和列表边界,以及 Core 与官方服务存在差异的地方。[OpenAI 兼容性](index.md) 列出了这些差异和尚存缺口;[Sessions, events and history](sessions-events.md)、[message content](message-content.md)、[Vaults](vaults.md)、[source Files and Skills](source-files.md) 和 [Environment files and Artifacts](environment-files.md) 分别负责各自资源的规则。
Expand Down Expand Up @@ -178,6 +178,8 @@ Agent 创建和更新正文以及 Session 创建中的内联 `agent`,会在其
| Function `parameters` 的字符串根 `type` 不是 `object` | null | `Invalid schema for function '<name>': schema must be a JSON Schema of 'type: "object"', got 'type: "<type>"'.` |
| `text.format` JSON schema 的字符串根 `type` 不是 `object` | null | `agent.text.format.schema must have top-level type "object"; got "<type>"`,Agent 请求上也会返回此消息 |

Session 创建和更新、Vault 创建、Credential 创建和更新、Environment Template 创建和更新以及 Session events,会在各自解码之前,按同样方式根据已锁定的正文形状进行检查。param 是 JSON 路径,例如 `metadata`、`input[0].role`、`input[0].content[0].type`、`events[0].input[0].role`、`auth.type`、`auth.access_token`、`env.KEY` 或 `name`。`input` 和函数结果的 `output` 接受字符串或数组,其他类型会报告 `expected a string or an array`。Session 的 `agent` 和 `environment` 以及 Template 的 `network` 使用各自的解析器,并先检查其是否为对象。已锁定 schema 未表达的规则以字段作为 param,并给出说明该规则的消息:Vault 和 Credential 的 `name`(去除首尾空白后 1–256 UTF-8 字节)、Template 的 `name`(1–256 个字符)、非空的 `auth.token` 和 `auth.access_token`、HTTPS 的 `auth.mcp_server_url` 和 `auth.refresh.token_endpoint`、RFC 3339 的 `auth.expires_at`、环境变量名(`env.<name>`),以及没有任何可更改内容的 Credential 更新(param 为 `auth`)。

在同一个对象内,Core 会先报告联合类型的 `type`,然后报告未知成员,再按文档顺序报告成员值,最后按固定 schema 的顺序报告缺失成员;先检查 tools,再检查 `text`,并在重复项和 schema 根检查之前检查整个对象。没有字符串根 `type` 的 schema 不会被检查。Function 和 output schema、`request_metadata` 和 `x_agents_core` 使用各自的解析器。更新正文和 Session 内联 Agent 会在查找 Agent 之前进行验证,因此属于当前租户、属于外部租户、缺失和格式错误的 Agent ID 会得到相同的响应。

即使所选 Harness 无法执行,Core 也会保存已锁定形状允许的值:任意长度的函数名称,以及 Harness 未声明的 reasoning、service tier、Web 搜索和 programmatic tool calling 取值。
Expand Down
36 changes: 29 additions & 7 deletions scripts/generate-public-api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,19 @@
SHAPES = ROOT / 'services/core/internal/api/official_shapes.gen.go'
CLIENT_TYPES = ROOT / 'packages/agents-client/src/generated/public-api.ts'
CORE_CLIENT_TYPES = ROOT / 'packages/agents-client/src/generated/core-api.ts'
# Agent configuration request bodies checked by Core's shape walker.
REQUEST_SHAPES = ('CreateAgentParams', 'UpdateAgentParams', 'SessionAgentConfigParam')
# Request bodies checked by Core's shape walker, with the members whose values
# have their own Core parser and are only checked to be objects here.
REQUEST_SHAPES = {
'CreateAgentParams': (), 'UpdateAgentParams': (), 'SessionAgentConfigParam': (),
'CreateAgentSessionParams': ('agent', 'environment'),
'UpdateAgentSessionParams': (),
'CreateVaultParams': (),
'CreateVaultCredentialParams': (),
'RotateVaultCredentialParams': (),
'CreateEnvironmentTemplateParams': ('network',),
'UpdateEnvironmentTemplateParams': ('network',),
'CreateSessionEventsParams': (),
}
HTTP_METHODS = {'get', 'post', 'put', 'patch', 'delete', 'head', 'options'}


Expand Down Expand Up @@ -192,7 +203,13 @@ def go_shape(document, schema, required=False):
kind = value.get('type')
if isinstance(kind, list):
kind, = set(kind) - {'null'}
if 'oneOf' in value:
if 'oneOf' in value and 'discriminator' not in value:
# A string or an array, such as the Session input.
string, array = value['oneOf']
if (string['type'], array['type']) != ('string', 'array'):
raise ValueError(f'Unsupported union: {value["oneOf"]}')
fields = ['kind: stringOrArrayValue', 'items: &' + go_shape(document, array['items'])]
elif 'oneOf' in value:
if value['discriminator']['propertyName'] != 'type':
raise ValueError(f'Unsupported union discriminator: {value["discriminator"]}')
variants = {}
Expand Down Expand Up @@ -224,19 +241,24 @@ def go_shape(document, schema, required=False):
return 'shape{' + ', '.join(fields) + '}'


def go_members(document, schema, skip=None):
def go_members(document, schema, skip=None, opaque=()):
# The walker rejects unknown members, so only closed objects project.
if schema.get('additionalProperties') is not False:
raise ValueError(f'Unsupported open object: {sorted(schema["properties"])}')
required = schema.get('required', [])
return '[]member{\n' + ''.join(f'{{{json.dumps(name)}, {go_shape(document, value, name in required)}}},\n' for name, value in schema['properties'].items() if name != skip) + '}'
def member(name, value):
if name not in opaque:
return go_shape(document, value, name in required)
fields = ['kind: openObject'] + (['required: true'] if name in required else []) + (['nullable: true'] if nullable(document, value) else [])
return 'shape{' + ', '.join(fields) + '}'
return '[]member{\n' + ''.join(f'{{{json.dumps(name)}, {member(name, value)}}},\n' for name, value in schema['properties'].items() if name != skip) + '}'


def request_shapes(document, owners):
lines = ['// Code generated by scripts/generate-public-api.py; DO NOT EDIT.', 'package api', '',
'// Pinned request shapes; x_agents_core is checked by its own parser.', 'var (']
for name in REQUEST_SHAPES:
members = go_members(document, resolve(document, '#/components/schemas/' + name))
for name, opaque in REQUEST_SHAPES.items():
members = go_members(document, resolve(document, '#/components/schemas/' + name), opaque=opaque)
if name in owners:
members = members[:-1] + '{"x_agents_core", shape{}},\n}'
lines.append(f'{name[0].lower() + name[1:]} = shape{{kind: objectValue, members: {members}}}')
Expand Down
Loading
Loading