问题
Antigravity 的工具 schema 转换会静默删除本地 $ref 指向的完整结果合同。引用字段最后可能变成 {},模型看不到原 schema 要求的嵌套字段、类型和枚举。
Direct Subagent 的 output_schema 和 Workflow 的 structured output 接受 JSON Schema。相同合同在本地参数校验和模型收到的工具声明之间发生语义漂移。
价值
让模型看到能够满足的结果合同,减少因隐藏要求而产生的校验失败和纠错回合。对于 provider 无法表达的 schema,调用前明确报错比悄悄删空约束更容易定位和修复。
审计边界
- 检查日期:2026-09-08。
- 固定源码:ed9dbc1018f890fd54375f5371990ddfee8af5df。
- 已验证:实际仓库本地
convertTools(..., true) → sanitizeSchemaForCca 完整纯转换链。
- 验证使用本地 converter 与从同一固定源码提取的 sanitizer;没有只凭 sanitizer 单点输出来推断上游转换结果。
- 未验证:Antigravity 线上请求、HTTP 响应、真实模型任务或 provider 内部处理;不声称已经观察到 HTTP 400、重试次数或成功率变化。
触发条件
- 通过 Antigravity route 使用具有本地
$ref 的工具参数 schema,例如 Direct/Workflow 的结构化结果。
- 被引用的定义承载模型必须满足的结构或约束。
- 出站工具转换先删除定义,再删除引用;该字段变成无结构说明的空 schema。
最小输入
{
"type": "object",
"properties": {
"answer": { "$ref": "#/$defs/Answer" }
},
"required": ["answer"],
"$defs": {
"Answer": {
"type": "object",
"properties": {
"verdict": { "type": "string", "enum": ["pass", "fail"] }
},
"required": ["verdict"]
}
}
}
将该 schema 作为一个工具的 parameters,依照 buildTools 的顺序执行:
const declarations = convertTools([{
name: "structured_output",
description: "Return the final result",
parameters: schema,
}], true);
const converted = declarations[0].functionDeclarations[0].parameters;
const wire = sanitizeSchemaForCca(converted);
已验证的完整链输出为:
{
"converted": {
"type": "object",
"properties": { "answer": { "$ref": "#/$defs/Answer" } },
"required": ["answer"]
},
"wire": {
"type": "object",
"properties": { "answer": {} },
"required": ["answer"]
}
}
verdict、它的类型、枚举和嵌套 required 已全部不在出站工具声明中。这里的 wire 指客户端转换后的声明,不是线上 provider 接收回执。
源码证据
已有保护与建议范围
结构化工具仍保留原 schema 供 Pi 参数校验使用,因此本问题不等于“错误 JSON 会被当作有效结果”。当前代码也会把部分不支持的长度/数值约束写入 description;这些保护不能恢复已删除的引用结构。
建议转换前有界展开能够表达的本地引用;递归引用、外部引用或超出边界的展开明确拒绝并说明原因。实现选择应保持 provider 适配边界,不复制一套子任务执行或校验 Runtime。
验收
Related: #234 Antigravity/Cursor provider、#155 Direct Subagent 结构化结果合同。本 Issue 跟踪两者交界处的具体 schema 转换缺陷。
问题
Antigravity 的工具 schema 转换会静默删除本地
$ref指向的完整结果合同。引用字段最后可能变成{},模型看不到原 schema 要求的嵌套字段、类型和枚举。Direct Subagent 的
output_schema和 Workflow 的 structured output 接受 JSON Schema。相同合同在本地参数校验和模型收到的工具声明之间发生语义漂移。价值
让模型看到能够满足的结果合同,减少因隐藏要求而产生的校验失败和纠错回合。对于 provider 无法表达的 schema,调用前明确报错比悄悄删空约束更容易定位和修复。
审计边界
convertTools(..., true)→sanitizeSchemaForCca完整纯转换链。触发条件
$ref的工具参数 schema,例如 Direct/Workflow 的结构化结果。最小输入
{ "type": "object", "properties": { "answer": { "$ref": "#/$defs/Answer" } }, "required": ["answer"], "$defs": { "Answer": { "type": "object", "properties": { "verdict": { "type": "string", "enum": ["pass", "fail"] } }, "required": ["verdict"] } } }将该 schema 作为一个工具的
parameters,依照buildTools的顺序执行:已验证的完整链输出为:
{ "converted": { "type": "object", "properties": { "answer": { "$ref": "#/$defs/Answer" } }, "required": ["answer"] }, "wire": { "type": "object", "properties": { "answer": {} }, "required": ["answer"] } }verdict、它的类型、枚举和嵌套 required 已全部不在出站工具声明中。这里的 wire 指客户端转换后的声明,不是线上 provider 接收回执。源码证据
subagents/src/prompt.ts:197-204:output_schema接受带任意属性的对象。shared/structured-output.ts:32-68:检查 JSON 形状、深度和节点边界后,Type.Unsafe(schema)保留调用方 schema,没有禁止本地引用。antigravity/google-conversion.ts:26-35与397-429:本地convertTools的sanitizeForOpenApi删除$defs/definitions,没有展开$ref。antigravity/provider.ts:81-84与157-166:第二步直接删除$ref,也没有把引用内容转成 description。antigravity/provider.ts:272-290:所有相关工具声明依次经过这两步转换。已有保护与建议范围
结构化工具仍保留原 schema 供 Pi 参数校验使用,因此本问题不等于“错误 JSON 会被当作有效结果”。当前代码也会把部分不支持的长度/数值约束写入 description;这些保护不能恢复已删除的引用结构。
建议转换前有界展开能够表达的本地引用;递归引用、外部引用或超出边界的展开明确拒绝并说明原因。实现选择应保持 provider 适配边界,不复制一套子任务执行或校验 Runtime。
验收
{}。Related: #234 Antigravity/Cursor provider、#155 Direct Subagent 结构化结果合同。本 Issue 跟踪两者交界处的具体 schema 转换缺陷。