From 3a656136b6f6ccf94d8103e8efe540d4fc0aa8aa Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 17 Apr 2026 14:41:08 +0900 Subject: [PATCH] feat(delegate-task): add resolveMetadataModel helper for model fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper that picks primary model with fallback to a secondary model (e.g., categoryModel → parentContext.model). Enforces consistent {providerID, modelID} shape. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../resolve-metadata-model.test.ts | 50 +++++++++++++++++++ .../delegate-task/resolve-metadata-model.ts | 21 ++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/tools/delegate-task/resolve-metadata-model.test.ts create mode 100644 src/tools/delegate-task/resolve-metadata-model.ts diff --git a/src/tools/delegate-task/resolve-metadata-model.test.ts b/src/tools/delegate-task/resolve-metadata-model.test.ts new file mode 100644 index 000000000..50b29f253 --- /dev/null +++ b/src/tools/delegate-task/resolve-metadata-model.test.ts @@ -0,0 +1,50 @@ +const { describe, test, expect } = require("bun:test") + +import { resolveMetadataModel } from "./resolve-metadata-model" + +const PRIMARY = { providerID: "openai", modelID: "gpt-5.4" } +const FALLBACK = { providerID: "anthropic", modelID: "claude-sonnet-4-6" } + +describe("resolveMetadataModel", () => { + describe("#given primary and fallback are both present", () => { + test("#when resolving #then returns primary", () => { + const result = resolveMetadataModel(PRIMARY, FALLBACK) + + expect(result).toEqual(PRIMARY) + }) + }) + + describe("#given only fallback is present", () => { + test("#when resolving #then returns fallback", () => { + const result = resolveMetadataModel(undefined, FALLBACK) + + expect(result).toEqual(FALLBACK) + }) + }) + + describe("#given only primary is present", () => { + test("#when resolving #then returns primary", () => { + const result = resolveMetadataModel(PRIMARY, undefined) + + expect(result).toEqual(PRIMARY) + }) + }) + + describe("#given both are undefined", () => { + test("#when resolving #then returns undefined", () => { + const result = resolveMetadataModel(undefined, undefined) + + expect(result).toBeUndefined() + }) + }) + + describe("#given primary has extra fields", () => { + test("#when resolving #then strips to providerID and modelID only", () => { + const extended = { providerID: "openai", modelID: "gpt-5.4", variant: "high", temperature: 0.7 } as const + + const result = resolveMetadataModel(extended, undefined) + + expect(result).toEqual({ providerID: "openai", modelID: "gpt-5.4" }) + }) + }) +}) diff --git a/src/tools/delegate-task/resolve-metadata-model.ts b/src/tools/delegate-task/resolve-metadata-model.ts new file mode 100644 index 000000000..3c68ed3ad --- /dev/null +++ b/src/tools/delegate-task/resolve-metadata-model.ts @@ -0,0 +1,21 @@ +import type { DelegatedModelConfig } from "./types" + +export interface MetadataModel { + providerID: string + modelID: string +} + +type ModelLike = Pick | MetadataModel + +export function resolveMetadataModel( + primary: ModelLike | undefined, + fallback: ModelLike | undefined, +): MetadataModel | undefined { + if (primary) { + return { providerID: primary.providerID, modelID: primary.modelID } + } + if (fallback) { + return { providerID: fallback.providerID, modelID: fallback.modelID } + } + return undefined +}