Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ function isPermissionPolicy(value: unknown): value is PermissionPolicy {
return typeof value === "string" && PERMISSION_POLICY_VALUES.has(value)
}

/**
* Resolve the effective harness kind from the config, defaulting to `"pi_core"` when absent.
* The runner already treats a missing harness.kind as pi_core, so this aligns the UI with
* what actually runs. The default is read-path only — it never writes kind back into the config.
*/
export function resolveHarnessKind(harness: {kind?: string} | null | undefined): string {
return typeof harness?.kind === "string" ? harness.kind : "pi_core"
}

export function useModelHarness({
schema,
config,
Expand Down Expand Up @@ -171,7 +180,7 @@ export function useModelHarness({
// carries through extra keys (e.g. `extras`) so a form edit never silently drops them. The picker
// is harness-filtered: selecting a model sets BOTH the model id and its provider, fed by the
// `/inspect` capability map below.
const harnessValue = typeof harness.kind === "string" ? (harness.kind as string) : null
const harnessValue = resolveHarnessKind(harness)
const isPiHarness = harnessValue === "pi_core" || harnessValue === "pi_agenta"
const llm = config.llm
const modelId = useMemo(() => modelIdFromConfig(llm), [llm])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Unit tests for resolveHarnessKind — the defaulting logic that aligns the UI with the runner's
* treatment of absent harness.kind as pi_core.
*
* The default is read-path only; resolveHarnessKind never writes kind back into the config.
* Runs under @agenta/entity-ui's own vitest runner.
*/
import {describe, expect, it} from "vitest"

import {resolveHarnessKind} from "../../src/DrillInView/SchemaControls/agentTemplate/useModelHarness"

describe("resolveHarnessKind", () => {
it("returns pi_core when harness is null", () => {
expect(resolveHarnessKind(null)).toBe("pi_core")
})

it("returns pi_core when harness is undefined", () => {
expect(resolveHarnessKind(undefined)).toBe("pi_core")
})

it("returns pi_core when harness.kind is absent", () => {
expect(resolveHarnessKind({})).toBe("pi_core")
})

it("returns pi_core when harness.kind is undefined", () => {
expect(resolveHarnessKind({kind: undefined})).toBe("pi_core")
})

it("returns pi_core when harness.kind is an empty object (edge case)", () => {
// This covers the {harness: {}} case from the issue — the runner treats it as pi_core
expect(resolveHarnessKind({} as {kind?: string})).toBe("pi_core")
})

it("does not override an explicit claude harness", () => {
expect(resolveHarnessKind({kind: "claude"})).toBe("claude")
})

it("does not override an explicit openai harness", () => {
expect(resolveHarnessKind({kind: "openai"})).toBe("openai")
})

it("does not override an explicit pi_agenta harness", () => {
expect(resolveHarnessKind({kind: "pi_agenta"})).toBe("pi_agenta")
})

it("does not override an explicit bedrock harness", () => {
expect(resolveHarnessKind({kind: "bedrock"})).toBe("bedrock")
})
})
Loading