11import type { ToolDefinition } from "@intx/types/runtime" ;
2- import { isKimiLeafProvider } from "../subagent/provider-family.js" ;
2+ import {
3+ isKimiLeafProvider ,
4+ isMuseSparkLeafProvider ,
5+ } from "../subagent/provider-family.js" ;
36
47/** Context used to decide whether a provider needs wire-schema rewrites. */
58export interface NormalizeToolDefsContext {
@@ -9,7 +12,7 @@ export interface NormalizeToolDefsContext {
912
1013/**
1114 * Shared primitives / view guidance for `present`. Used by both the canonical
12- * `presentDefinition.description` and the kimi wire `view.description` so the
15+ * `presentDefinition.description` and the acyclic wire `view.description` so the
1316 * two never drift.
1417 */
1518export const PRESENT_VIEW_PRIMITIVES_GUIDANCE =
@@ -114,10 +117,11 @@ const COLUMNS_PROP = {
114117} as const ;
115118
116119/**
117- * Non-recursive `present` parameters for Moonshot/kimi-class backends.
118- * Those providers reject JSON Schema `$ref` cycles on `tools.function.parameters`
119- * (recursive ViewNode). This shape inlines depth-capped oneOf primitives so the
120- * model still sees type/children/text fields — not a bare freeform object.
120+ * Non-recursive `present` parameters for backends that reject JSON Schema `$ref`
121+ * cycles on `tools.function.parameters` (recursive ViewNode). Moonshot/Kimi and
122+ * Meta Muse Spark both fail the turn before inference when the cycle is on the
123+ * wire. This shape inlines depth-capped oneOf primitives so the model still
124+ * sees type/children/text fields — not a bare freeform object.
121125 * Runtime still validates full nested trees via `validateView`.
122126 */
123127export const KIMI_PRESENT_INPUT_SCHEMA = {
@@ -180,7 +184,7 @@ export const KIMI_PRESENT_INPUT_SCHEMA = {
180184 required : [ "view" ] ,
181185} as const ;
182186
183- function rewritePresentForKimi ( def : ToolDefinition ) : ToolDefinition {
187+ function rewritePresentAcyclic ( def : ToolDefinition ) : ToolDefinition {
184188 return {
185189 ...def ,
186190 // structuredClone so callers cannot mutate the shared const via the tool def.
@@ -190,13 +194,17 @@ function rewritePresentForKimi(def: ToolDefinition): ToolDefinition {
190194 } ;
191195}
192196
197+ function needsAcyclicPresentSchema ( ctx : NormalizeToolDefsContext ) : boolean {
198+ return isKimiLeafProvider ( ctx ) || isMuseSparkLeafProvider ( ctx ) ;
199+ }
200+
193201/**
194202 * Family-gated wire rewrite of tool definitions before they reach the director /
195- * provider. Today only Moonshot/kimi get a non-recursive `present` schema;
203+ * provider. Moonshot/kimi and Muse Spark get a non-recursive `present` schema;
196204 * other providers receive the definitions unchanged (identity).
197205 *
198206 * Does not alter runtime validation or the canonical `presentDefinition` used
199- * as the source of truth for non-kimi advertise paths .
207+ * as the source of truth for providers that accept `$ref` cycles .
200208 *
201209 * Call this at every advertise path that may include `present` (main TUI/exec
202210 * sessions). Sub-agent toolsets currently omit `present` (main-session only);
@@ -206,10 +214,10 @@ export function normalizeToolDefinitionsForProvider(
206214 defs : readonly ToolDefinition [ ] ,
207215 ctx : NormalizeToolDefsContext ,
208216) : ToolDefinition [ ] {
209- if ( ! isKimiLeafProvider ( ctx ) ) {
217+ if ( ! needsAcyclicPresentSchema ( ctx ) ) {
210218 return defs as ToolDefinition [ ] ;
211219 }
212220 return defs . map ( ( def ) =>
213- def . name === "present" ? rewritePresentForKimi ( def ) : def ,
221+ def . name === "present" ? rewritePresentAcyclic ( def ) : def ,
214222 ) ;
215223}
0 commit comments