diff --git a/packages/sdk/src/internal/providers/bailian/mapper.ts b/packages/sdk/src/internal/providers/bailian/mapper.ts index 9802bbe..462c234 100644 --- a/packages/sdk/src/internal/providers/bailian/mapper.ts +++ b/packages/sdk/src/internal/providers/bailian/mapper.ts @@ -290,19 +290,18 @@ export function mapAgent( } } - // Skills - if (refs.skill_ids.length) { - body.skills = refs.skill_ids.map((s) => ({ - // Bailian's SkillType enum is "customer" | "official"; map the - // resolver's generic "custom" sentinel to "customer". - type: s.type === "custom" ? "customer" : s.type, - skill_id: s.skill_id, - // Bailian composes `{skill_id}@{version}` internally and rejects - // entries without a version. Prefer explicit external references, then - // the latest active remote version, then the common initial version. - version: s.version ?? skillVersions?.[s.skill_id] ?? "1.0", - })); - } + // Skills are an explicit replacement set. Keep an empty array in the full update + // payload so removing the last binding has the same declarative meaning everywhere. + body.skills = refs.skill_ids.map((s) => ({ + // Bailian's SkillType enum is "customer" | "official"; map the + // resolver's generic "custom" sentinel to "customer". + type: s.type === "custom" ? "customer" : s.type, + skill_id: s.skill_id, + // Bailian composes `{skill_id}@{version}` internally and rejects + // entries without a version. Prefer explicit external references, then + // the latest active remote version, then the common initial version. + version: s.version ?? skillVersions?.[s.skill_id] ?? "1.0", + })); return body; } diff --git a/packages/sdk/src/internal/providers/qoder/mapper.ts b/packages/sdk/src/internal/providers/qoder/mapper.ts index b4f3bf0..3bf2113 100644 --- a/packages/sdk/src/internal/providers/qoder/mapper.ts +++ b/packages/sdk/src/internal/providers/qoder/mapper.ts @@ -466,13 +466,12 @@ export function mapAgent( } } - // Skills - if (refs.skill_ids.length) { - body.skills = refs.skill_ids.map((s) => ({ - type: s.type === "official" ? "qoder" : s.type, - skill_id: s.skill_id, - })); - } + // Skills are an explicit replacement set. Qoder agent updates use merge semantics, + // so omitting this field would retain remote bindings when the declaration is `skills: []`. + body.skills = refs.skill_ids.map((s) => ({ + type: s.type === "official" ? "qoder" : s.type, + skill_id: s.skill_id, + })); return body; } diff --git a/packages/sdk/tests/unit/bailian.test.ts b/packages/sdk/tests/unit/bailian.test.ts index b1312f0..2cbfe6e 100644 --- a/packages/sdk/tests/unit/bailian.test.ts +++ b/packages/sdk/tests/unit/bailian.test.ts @@ -222,6 +222,11 @@ describe("Bailian mapAgent", () => { expect(body.skills).toEqual([{ type: "customer", skill_id: "skill_abc", version: "1.0" }]); }); + test("keeps an explicit empty skill replacement set", () => { + const body = mapAgent("helper", minimalDecl, emptyRefs) as Record; + expect(body.skills).toEqual([]); + }); + test("injects agents metadata when projectName provided", () => { const body = mapAgent("helper", minimalDecl, emptyRefs, undefined, "my-project") as Record; const meta = body.metadata as Record; diff --git a/packages/sdk/tests/unit/qoder-examples.test.ts b/packages/sdk/tests/unit/qoder-examples.test.ts index 2691205..02d2655 100644 --- a/packages/sdk/tests/unit/qoder-examples.test.ts +++ b/packages/sdk/tests/unit/qoder-examples.test.ts @@ -97,6 +97,7 @@ test("qoder agent mapper preserves declared tool permission policies", () => { ], }, ]); + expect(body.skills).toEqual([]); }); test("qoder permission overrides are case- and separator-insensitive", () => {