Distinguish implicit harness fallback pins

Signed-off-by: kenny lopez <klopez4212@gmail.com>
This commit is contained in:
kenny lopez
2026-07-27 13:31:59 +01:00
parent 7b46102d04
commit fdc1ef2c6d
40 changed files with 245 additions and 43 deletions
+5 -1
View File
@@ -98,7 +98,11 @@ with a TypeScript lookup table or an id comparison in a component.
without changing the persisted configuration used by existing agents.
Legacy global defaults with no saved preferred runtime are treated as
Buzz Agent-owned for this masking boundary, so hiding Buzz Agent cannot
leak its provider/model into another implicit fallback.
leak its provider/model into another implicit fallback. A persisted
per-instance harness override carries a durable explicit-vs-implicit marker:
explicit selections continue to inherit global provider/model defaults even
when the selected harness differs from the global preference, while
automatic runtime-less fallback pins keep mismatched defaults masked.
Create-mode dialogs use the implicit masked config; existing definition and
instance edit dialogs use the raw persisted config.
`resolvePersonaRuntime` is the shared visibility boundary for every
@@ -66,6 +66,8 @@ export type CreateChannelManagedAgentInput = {
* unpinned so the persona remains authoritative.
*/
harnessOverride?: boolean;
/** The pin preserves an automatic runtime-less fallback, not an explicit pick. */
implicitHarnessFallback?: boolean;
/** Preferred model ID from the persona. Passed to createManagedAgent. */
model?: string;
role?: Exclude<ChannelRole, "owner">;
@@ -355,6 +357,7 @@ export async function provisionChannelManagedAgent(
acpCommand: "buzz-acp",
agentCommand: input.runtime.command,
harnessOverride: input.harnessOverride ?? false,
implicitHarnessFallback: input.implicitHarnessFallback ?? false,
// Do NOT seed agentArgs from runtime.defaultArgs (see instanceInputForDefinition.ts
// for the rationale — empty args let spawn resolve definition args live).
agentArgs: [],
@@ -84,12 +84,14 @@ test("row 2: harnessOverride follows the backend-aligned formula", async () => {
gooseRuntime,
);
assert.equal(match.harnessOverride, true, "picked == configured → true");
assert.equal(match.implicitHarnessFallback, false);
const noPreference = await buildInstanceInputForDefinition(
persona({ runtime: undefined }),
gooseRuntime,
);
assert.equal(noPreference.harnessOverride, true, "no preference → true");
assert.equal(noPreference.implicitHarnessFallback, true);
const differs = await buildInstanceInputForDefinition(
persona({ runtime: "claude" }),
@@ -100,6 +102,7 @@ test("row 2: harnessOverride follows the backend-aligned formula", async () => {
false,
"picked != configured → false (definition stays authoritative)",
);
assert.equal(differs.implicitHarnessFallback, false);
});
test("provisioning pins a visible fallback for a runtime-less definition", () => {
@@ -111,6 +114,7 @@ test("provisioning pins a visible fallback for a runtime-less definition", () =>
);
assert.equal(resolved.runtime, gooseRuntime);
assert.equal(resolved.harnessOverride, true);
assert.equal(resolved.implicitHarnessFallback, true);
});
test("provisioning uses product ordering and leaves unavailable configured fallbacks unpinned", () => {
@@ -121,12 +125,14 @@ test("provisioning uses product ordering and leaves unavailable configured fallb
]);
assert.equal(ordered.runtime, buzzAgentRuntime);
assert.equal(ordered.harnessOverride, true);
assert.equal(ordered.implicitHarnessFallback, true);
const unavailable = resolveProvisioningRuntimeForDefinition("missing", [
gooseRuntime,
]);
assert.equal(unavailable.runtime, gooseRuntime);
assert.equal(unavailable.harnessOverride, false);
assert.equal(unavailable.implicitHarnessFallback, false);
});
test("row 3: plain avatar URLs pass through; base64 data URIs upload via the injectable", async () => {
@@ -205,6 +211,7 @@ test("no backend intent is byte-identical to the pre-intent mapping", async () =
agentArgs: [],
mcpCommand: "goose-mcp",
harnessOverride: true,
implicitHarnessFallback: false,
model: undefined,
provider: undefined,
spawnAfterCreate: true,
@@ -120,7 +120,10 @@ export function resolveProvisioningRuntimeForDefinition(
runtimes: readonly AcpRuntime[],
preferredRuntimeId?: string | null,
disabledRuntimeIds: readonly string[] = getDisabledAcpRuntimeIdsSnapshot(),
): ResolvePersonaRuntimeResult & { harnessOverride: boolean } {
): ResolvePersonaRuntimeResult & {
harnessOverride: boolean;
implicitHarnessFallback: boolean;
} {
const defaultRuntime = getDefaultPersonaRuntime(runtimes, preferredRuntimeId);
const resolved = resolvePersonaRuntime(
definitionRuntimeId,
@@ -137,6 +140,8 @@ export function resolveProvisioningRuntimeForDefinition(
definitionRuntimeId,
resolved.runtime.id,
),
implicitHarnessFallback:
resolved.runtime !== null && !definitionRuntimeId?.trim(),
};
}
@@ -207,6 +212,7 @@ export async function buildInstanceInputForDefinition(
persona.runtime,
runtime.id,
),
implicitHarnessFallback: !persona.runtime?.trim(),
model: persona.model ?? undefined,
provider: persona.provider ?? undefined,
spawnAfterCreate: true,
@@ -160,6 +160,7 @@ export function AddTeamToChannelDialog({
persona.runtime,
personaRuntime.id,
),
implicitHarnessFallback: !persona.runtime?.trim(),
model: persona.model ?? undefined,
personaId: persona.id,
teamId: team.id,
@@ -93,6 +93,7 @@ export function useApplyTemplate() {
name: persona.displayName,
personaId: persona.id,
harnessOverride: resolved.harnessOverride,
implicitHarnessFallback: resolved.implicitHarnessFallback,
systemPrompt: persona.systemPrompt,
avatarUrl: persona.avatarUrl ?? undefined,
model: entry.model ?? persona.model ?? undefined,
@@ -121,6 +122,7 @@ export function useApplyTemplate() {
name: persona.displayName,
personaId: persona.id,
harnessOverride: resolved.harnessOverride,
implicitHarnessFallback: resolved.implicitHarnessFallback,
systemPrompt: persona.systemPrompt,
avatarUrl: persona.avatarUrl ?? undefined,
model: teamEntry.model ?? persona.model ?? undefined,
@@ -156,6 +156,7 @@ export function AddChannelBotDialog({
name: persona.displayName,
personaId: persona.id,
harnessOverride: resolved.harnessOverride,
implicitHarnessFallback: resolved.implicitHarnessFallback,
systemPrompt: persona.systemPrompt,
avatarUrl: persona.avatarUrl ?? undefined,
model: persona.model ?? undefined,
@@ -32,7 +32,7 @@ export function useQuickBotDrop(channelId: string | null) {
setState({ pending: true, error: null });
try {
const { harnessOverride, runtime } =
const { harnessOverride, implicitHarnessFallback, runtime } =
resolveProvisioningRuntimeForDefinition(persona.runtime, providers);
if (!runtime) {
@@ -51,6 +51,7 @@ export function useQuickBotDrop(channelId: string | null) {
personaId: persona.id,
model: persona.model ?? undefined,
harnessOverride,
implicitHarnessFallback,
});
setState({ pending: false, error: null });
@@ -326,7 +326,7 @@ export function useMentionSendFlow({
}
seenPersonaIds.add(persona.id);
const { harnessOverride, runtime } =
const { harnessOverride, implicitHarnessFallback, runtime } =
resolveProvisioningRuntimeForDefinition(persona.runtime, runtimes);
if (!runtime) {
errors.push(`${displayName}: No agent runtime available.`);
@@ -342,6 +342,7 @@ export function useMentionSendFlow({
name: persona.displayName,
personaId: persona.id,
harnessOverride,
implicitHarnessFallback,
systemPrompt: persona.systemPrompt,
avatarUrl: persona.avatarUrl ?? undefined,
model: persona.model ?? undefined,
+1
View File
@@ -874,6 +874,7 @@ export async function createManagedAgent(input: CreateManagedAgentInput) {
acpCommand: input.acpCommand,
agentCommand: input.agentCommand,
harnessOverride: input.harnessOverride ?? false,
implicitHarnessFallback: input.implicitHarnessFallback ?? false,
agentArgs: input.agentArgs,
mcpCommand: input.mcpCommand,
turnTimeoutSeconds: input.turnTimeoutSeconds,
+5
View File
@@ -456,6 +456,11 @@ export type CreateManagedAgentInput = {
* same persona runtime id, while still ignoring missing-runtime fallbacks.
*/
harnessOverride?: boolean;
/**
* True when `harnessOverride` pins an automatically selected runtime for a
* runtime-less persona rather than a runtime the user explicitly selected.
*/
implicitHarnessFallback?: boolean;
agentArgs?: string[];
mcpCommand?: string;
turnTimeoutSeconds?: number;