fix(desktop): clarify inherited agent parallelism (#4010)

## Summary

- show an unambiguous `App default (10)` inherited state for parallelism
in create and edit forms
- explain that blank inherits the app default and suppress create-form
number steppers that could silently set `1`
- align the E2E mint fallback with production while preserving explicit
input → definition → app-default precedence

## Why

The forms displayed `1` even though an untouched field is omitted and
desktop minting materializes `10`. The create-form spinner could also
turn blank/inherited into an explicit `1` with one click while leaving
the field looking nearly unchanged.

## Testing

- `pnpm test` (desktop: 3,886 passed)
- `pnpm typecheck` (desktop)
- `pnpm check` (desktop)
- pre-push `desktop-check` and `desktop-test`

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
This commit is contained in:
Wes
2026-08-03 14:47:52 -06:00
committed by GitHub
parent 5c98932c59
commit d4a4570b97
6 changed files with 64 additions and 10 deletions

View File

@@ -250,12 +250,16 @@ fn generated_passphrase_respects_word_count_and_separator() {
#[test]
fn generated_passphrase_clamps_word_count() {
// Use a separator that cannot appear in the EFF wordlist so a generated
// word such as "yo-yo" cannot be mistaken for two words.
const SEPARATOR: &str = "|";
// Below the floor: clamped up to MIN_PASSPHRASE_WORDS, never shorter.
let phrase = generate_passphrase(1, "-").unwrap();
assert_eq!(phrase.split('-').count(), MIN_PASSPHRASE_WORDS);
let phrase = generate_passphrase(1, SEPARATOR).unwrap();
assert_eq!(phrase.split(SEPARATOR).count(), MIN_PASSPHRASE_WORDS);
// Above the ceiling: clamped down to MAX_PASSPHRASE_WORDS.
let phrase = generate_passphrase(50, "-").unwrap();
assert_eq!(phrase.split('-').count(), MAX_PASSPHRASE_WORDS);
let phrase = generate_passphrase(50, SEPARATOR).unwrap();
assert_eq!(phrase.split(SEPARATOR).count(), MAX_PASSPHRASE_WORDS);
}
#[test]

View File

@@ -0,0 +1,20 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
DEFAULT_AGENT_PARALLELISM,
resolveAgentParallelism,
} from "./agentParallelism.ts";
test("parallelism uses the app default only when input and definition omit it", () => {
assert.equal(
resolveAgentParallelism(undefined, undefined),
DEFAULT_AGENT_PARALLELISM,
);
assert.equal(
resolveAgentParallelism(undefined, null),
DEFAULT_AGENT_PARALLELISM,
);
assert.equal(resolveAgentParallelism(undefined, 4), 4);
assert.equal(resolveAgentParallelism(2, 4), 2);
});

View File

@@ -0,0 +1,18 @@
/**
* Desktop-managed agents materialize this value when neither the create input
* nor the linked definition sets parallelism. Keep in sync with
* `managed_agents::DEFAULT_AGENT_PARALLELISM` in the Tauri backend.
*/
export const DEFAULT_AGENT_PARALLELISM = 10;
export const AGENT_PARALLELISM_PLACEHOLDER = `App default (${DEFAULT_AGENT_PARALLELISM})`;
export const AGENT_PARALLELISM_HELP = `Leave blank to use the app default (currently ${DEFAULT_AGENT_PARALLELISM}). Custom values may be 1–32.`;
export const EDIT_AGENT_PARALLELISM_HELP =
"Current value for this agent. Custom values may be 1–32.";
export function resolveAgentParallelism(
input: number | undefined,
definition: number | null | undefined,
): number {
return input ?? definition ?? DEFAULT_AGENT_PARALLELISM;
}

View File

@@ -11,6 +11,7 @@ import {
import type { AgentPersona } from "@/shared/api/types";
import { BuzzAgentModelTuningFields } from "./buzzAgentModelTuningFields";
import { isBuzzAgentRuntime } from "./buzzAgentConfig";
import { EDIT_AGENT_PARALLELISM_HELP } from "../lib/agentParallelism";
export function EditAgentAdvancedFields({
acpCommand,
@@ -173,10 +174,14 @@ export function EditAgentAdvancedFields({
id="edit-agent-parallelism"
inputMode="numeric"
onChange={(event) => onParallelismChange(event.target.value)}
placeholder="1"
placeholder="Current value"
type="text"
value={parallelism}
/>
</div>
<p className="text-xs text-muted-foreground">
{EDIT_AGENT_PARALLELISM_HELP}
</p>
</div>
{/* Relay URL: intentionally no editor. The legacy per-record relay pin

View File

@@ -4,6 +4,10 @@ import { EnvVarsEditor, type EnvVarsValue } from "./EnvVarsEditor";
import { CreateAgentRespondToField } from "./RespondToField";
import type { PersonaBehaviorDraft } from "./personaBehaviorDraft";
import { isBuzzAgentRuntime } from "./buzzAgentConfig";
import {
AGENT_PARALLELISM_HELP,
AGENT_PARALLELISM_PLACEHOLDER,
} from "../lib/agentParallelism";
import { BuzzAgentModelTuningFields } from "./buzzAgentModelTuningFields";
import {
CARD_MINT_KEY_ANNOTATIONS,
@@ -83,7 +87,7 @@ export function PersonaAdvancedFields({
>
<Input
className={cn(
"h-8 px-0 py-0 leading-6",
"h-8 px-0 py-0 leading-6 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
PERSONA_FIELD_CONTROL_CLASS,
)}
disabled={disabled}
@@ -97,13 +101,13 @@ export function PersonaAdvancedFields({
parallelism: event.target.value,
})
}
placeholder="1"
placeholder={AGENT_PARALLELISM_PLACEHOLDER}
type="number"
value={behaviorDraft.parallelism}
/>
</div>
<p className="text-xs text-muted-foreground">
How many conversations each running instance handles at once (1–32).
{AGENT_PARALLELISM_HELP}
</p>
</div>
</div>

View File

@@ -12,6 +12,7 @@ import {
import { relayClient } from "@/shared/api/relayClient";
import { activateRateLimit } from "@/shared/api/relayRateLimitGate";
import { resolveAgentParallelism } from "@/features/agents/lib/agentParallelism";
import type { ConnectionState } from "@/shared/api/relayClientShared";
import type { ChannelTemplate, RelayEvent } from "@/shared/api/types";
import { getMarkdownParseCount } from "@/shared/ui/markdown/nodeCache";
@@ -8075,8 +8076,10 @@ async function handleCreateManagedAgent(
args.input.respondTo !== undefined
? (args.input.respondToAllowlist ?? [])
: (linkedPersona?.respond_to_allowlist ?? []);
const mintParallelism =
args.input.parallelism ?? linkedPersona?.parallelism ?? 1;
const mintParallelism = resolveAgentParallelism(
args.input.parallelism,
linkedPersona?.parallelism,
);
const personaAvatarUrl =
args.input.personaId === undefined
? null