Skip to content

Latest commit

 

History

History
67 lines (48 loc) · 5.18 KB

File metadata and controls

67 lines (48 loc) · 5.18 KB

Adapter registry authority

Decision

Runtime adapter construction has one authority: src/adapters/registry.ts.

src/server/adapter-resolve.ts may resolve a provider/model onto an adapter id, but it does not maintain a second adapter factory inventory. The selected persisted/configured adapter id remains an untrusted string until the registry lookup succeeds. Unknown ids fail with the existing Unknown adapter: <id> error instead of widening configuration types around a closed compile-time union.

Semantic inheritance is not constructor inheritance

Some adapters share another adapter's routed-tool semantics while retaining independent runtime construction:

  • azure and azure-openai inherit the openai-responses contract.
  • mimo-free inherits the openai-chat contract.
  • cursor stays direct because its runTurn transport and gated native-file fallback are distinct.

The registry records those relationships with contractParent. A parent relationship does not mean the registry recursively constructs a parent adapter and injects it into the child. Azure and MiMo keep owning their existing internal composition. This avoids making production constructors depend on test/conformance needs and keeps this authority refactor behavior-neutral.

Wrapper-cycle and runtime validation policy

effectiveAdapterContract() follows contractParent links at runtime with a visited set. Unknown parents and cycles fail closed. This is intentionally runtime validation: registry/config values can originate in persisted files written by older or hand-edited installations, so compile-time typing alone is not an adequate boundary.

Extension policy

Adding a production adapter requires:

  1. one ADAPTER_REGISTRY entry with its factory;
  2. either a direct wire + mutation contract or an explicit contractParent;
  3. provider/model adapter ids that point only at registered ids;
  4. registry-derived conformance coverage in the follow-up conformance layer.

Do not add a second switch/list of adapter factories in request routing. Focused tests may construct a concrete adapter directly when they are testing that adapter itself; cross-adapter production routing should use registry authority.

Scope boundary

This decision does not change routed apply_patch behavior, Cursor structured-edit conversion, Azure/MiMo request construction, or provider wire selection. Those behaviors remain owned by their existing modules and focused tests. The registry exposes the universe and semantic relationships; the next stack layer consumes that metadata for generic conformance.

Moonshot $ref-with-siblings normalization

Moonshot/Kimi enforce the draft-07 reading where $ref must stand alone and 400 the whole request when a node carries both. Codex's own deferred tool catalog emits exactly that shape, so the schema is not something a user can fix from configuration (issue #2673).

[Decision Log]

  • 목적과 의도: Codex가 내보내는 $ref + 형제 키워드 스키마를 Moonshot이 받아들이는 형태로 바꾸되, 도구가 실제로 요구하는 제약을 잃지 않는다.
  • 기존 구현 및 제약 조건: JSON Schema 2020-12에서 $ref는 in-place applicator라 형제 키워드와 함께 적용된다. Moonshot은 이를 거부하므로 참조 대상을 노드 아래로 인라인해야 하고, 재귀 스키마는 유한해야 하며, 어댑터는 요청 경로에 있으므로 지연이 그대로 사용자에게 간다.
  • 검토한 주요 대안: (1) 형제 키워드를 버리고 순수 $ref만 남긴다. (2) 참조를 인라인하되 형제 키워드가 대상을 덮어쓴다. (3) 인라인하되 집합형 어서션은 합집합으로 합치고 나머지는 좁히는 쪽이 이긴다. (4) allOf로 감싼다.
  • 선택한 방식: (3). required는 합집합, properties는 병합, 나머지 키워드는 노드가 이긴다. 해석 불가능한 참조는 순수 $ref로 남기고, 깊이·노드·확장 예산을 각각 둔다.
  • 다른 대안 대신 이 방식을 선택한 이유: (1)은 노드가 좁힌 제약을 통째로 버린다. (2)는 대상이 요구하던 a를 형제의 b가 덮어써서, 양쪽 어느 쪽도 요청하지 않은 더 약한 계약을 조용히 내보냈다 — 리뷰가 지적한 정확한 결함이다. (4)는 Moonshot이 allOf를 어떻게 다루는지 확인된 근거가 없어 검증되지 않은 가정을 계약으로 만든다.
  • 장점, 단점 및 영향: 도구 계약이 보존된 채 Moonshot을 통과한다. 인라인은 대상을 복제하므로 큰 정의를 여러 노드가 참조하면 출력이 커질 수 있고, 예산이 소진되면 해당 노드는 빈 객체나 순수 $ref로 닫힌다 — 약해진 스키마를 절반만 내보내는 것보다 낫다. Moonshot 계열 openai-chat baseUrl에만 적용되고 다른 provider는 손대지 않는다.

예산은 세 가지다. 확장 횟수만으로는 참조가 하나도 없는 깊은 스키마를 막지 못해서, 깊이와 노드 수를 따로 둔다 — google-tool-schema.ts가 이미 쓰는 형태다. 두 가드 모두 제거했을 때 실제로 red가 되는지 확인했고, 예산을 풀면 20k 깊이에서 RangeError: Maximum call stack size exceeded가 난다.