Files
buzz/desktop/src/features/onboarding/welcomeGuide.test.mjs
Tom Brow 16cc3de6d6 fix(desktop): enforce owner-only access in internal builds (#4053)
## Problem

Managed agents in internal Buzz builds should answer only their owner.
Previously, an agent could keep a broader access setting and respond to
other people, which did not match the access policy for internal builds.

This PR makes owner-only access effective for every managed agent in
internal builds and makes that restriction clear in the Desktop UI. Open
source builds remain configurable.

## Changes

- Enforce owner-only access when any managed agent starts or is deployed
from an internal build.
- Show the agent access control as locked to **Only me** in Desktop,
with an explanation of why it cannot be changed.
- Keep Welcome teammates working under the same rule without triggering
unnecessary restarts.
- Leave open source build behavior unchanged. This changes effective
runtime access without rewriting stored or relay-advertised settings.

The companion [#4064](https://github.com/block/buzz/pull/4064) explains
the restriction in-thread when someone without access mentions an agent.

The enforcement will remain inactive in shipped builds until
[squareup/buzz-releases#74](https://github.com/squareup/buzz-releases/pull/74)
marks internal releases during the build.

## Screenshots

| Before | After |
| --- | --- |
| ![Editable agent access control before the
change](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/4053/4053-before.png)
| ![Agent access locked to Only me in an internal
build](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/4053/4053-after-v2.png)
|

## Tests

Added coverage for:

- Runtime enforcement for [locally run
agents](e0165f52b5/desktop/src-tauri/src/managed_agents/runtime/tests.rs (L196))
and [deployed
agents](e0165f52b5/desktop/src-tauri/src/commands/agents_tests.rs (L510)).
- The [current-build deployment
path](e0165f52b5/desktop/src-tauri/src/commands/agents_tests.rs (L455)),
[invalid stored
access](e0165f52b5/desktop/src-tauri/src/managed_agents/access_policy.rs (L98)),
and the [local startup
guard](e0165f52b5/desktop/src-tauri/src/managed_agents/env_vars/tests.rs (L149)).
- Consistent enforcement across [both agent
backends](e0165f52b5/desktop/src-tauri/src/managed_agents/access_policy.rs (L112)).
- Welcome teammates created as [locally
run](e0165f52b5/desktop/src/features/onboarding/welcomeGuide.test.mjs (L384))
or
[deployed](e0165f52b5/desktop/src/features/onboarding/welcomeGuide.test.mjs (L393))
agents, including
[access-only](e0165f52b5/desktop/src/features/onboarding/welcomeKickoff.test.mjs (L202))
and
[runtime-related](e0165f52b5/desktop/src/features/onboarding/welcomeKickoff.test.mjs (L225))
restart behavior.

The full Desktop Rust and JavaScript suites, type checks, formatting,
clippy, and file-size checks passed. Playwright E2E was not run.

---

Originated from Buzz channel
[buzz-agent-control](buzz://channel?id=cf5dada7-e26a-4887-ae41-b3bd5f42d3b2).
Supersedes #2537.

---------

Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: npub1tquskdu6yc4h8l7xxtceculxw600grekeq0xg2ukqfrwl7vrzg3quz3gmp <58390b379a262b73ffc632f19c73e6769ef40f36c81e642b960246eff9831222@buzz.block.builderlab.xyz>
Co-authored-by: npub1tquskdu6yc4h8l7xxtceculxw600grekeq0xg2ukqfrwl7vrzg3quz3gmp <58390b379a262b73ffc632f19c73e6769ef40f36c81e642b960246eff9831222@buzz.block.builderlab.xyz>
Co-authored-by: Amp <amp@ampcode.com>
2026-08-05 19:58:24 -06:00

455 lines
12 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
activateWelcomeTeamPersonasSequentially,
buildWelcomeStarterCreateInput,
LEGACY_WELCOME_GUIDE_SYSTEM_PROMPT,
pickWelcomeGuideAgent,
pickWelcomeGuideAgentForRelay,
pickWelcomeTeamStarterAgentForRelay,
welcomeStarterRuntimeUpdate,
welcomeTeammateAccessUpdate,
welcomeTeammateHasExpectedAccess,
WELCOME_GUIDE_AGENT_NAME,
WELCOME_GUIDE_PERSONA_ID,
WELCOME_TEAM_ID,
WELCOME_TEAM_STARTERS,
} from "./welcomeGuide.ts";
const PUB_A = "a".repeat(64);
const PUB_B = "b".repeat(64);
const PUB_C = "c".repeat(64);
const RELAY_A = "ws://localhost:3000";
const RELAY_B = "ws://localhost:3001";
function makeAgent(overrides = {}) {
return {
pubkey: PUB_A,
name: WELCOME_GUIDE_AGENT_NAME,
personaId: null,
relayUrl: RELAY_A,
acpCommand: "buzz-acp",
agentCommand: "buzz-agent",
agentCommandOverride: null,
agentArgs: [],
mcpCommand: "buzz-dev-mcp",
turnTimeoutSeconds: 120,
idleTimeoutSeconds: null,
maxTurnDurationSeconds: null,
parallelism: 1,
systemPrompt: null,
model: null,
provider: null,
envVars: {},
status: "stopped",
pid: null,
createdAt: "2026-06-11T00:00:00.000Z",
updatedAt: "2026-06-11T00:00:00.000Z",
lastStartedAt: null,
lastStoppedAt: null,
lastExitCode: null,
lastError: null,
logPath: "",
startOnAppLaunch: false,
backend: { type: "local" },
backendAgentId: null,
respondTo: "owner-only",
respondToAllowlist: [],
teamId: WELCOME_TEAM_ID,
...overrides,
};
}
test("pickWelcomeGuideAgent reuses a legacy Kit guide", () => {
const legacyKit = makeAgent({
name: "Kit",
pubkey: PUB_A,
systemPrompt: LEGACY_WELCOME_GUIDE_SYSTEM_PROMPT,
});
assert.equal(pickWelcomeGuideAgent([legacyKit]), legacyKit);
});
test("pickWelcomeGuideAgent prefers a running legacy guide over stopped builtin Fizz", () => {
const stoppedBuiltinFizz = makeAgent({
pubkey: PUB_A,
personaId: WELCOME_GUIDE_PERSONA_ID,
status: "stopped",
});
const runningLegacyKit = makeAgent({
name: "Kit",
pubkey: PUB_B,
status: "running",
systemPrompt: LEGACY_WELCOME_GUIDE_SYSTEM_PROMPT,
});
assert.equal(
pickWelcomeGuideAgent([stoppedBuiltinFizz, runningLegacyKit]),
runningLegacyKit,
);
});
test("pickWelcomeGuideAgent ignores non-Kit agents with the legacy prompt", () => {
const nonKit = makeAgent({
pubkey: PUB_A,
name: "Scout",
systemPrompt: LEGACY_WELCOME_GUIDE_SYSTEM_PROMPT,
});
const fizz = makeAgent({
pubkey: PUB_C,
personaId: WELCOME_GUIDE_PERSONA_ID,
});
assert.equal(pickWelcomeGuideAgent([nonKit, fizz]), fizz);
});
test("pickWelcomeGuideAgentForRelay ignores Fizz agents from other communities", () => {
const otherCommunityFizz = makeAgent({
pubkey: PUB_A,
personaId: WELCOME_GUIDE_PERSONA_ID,
relayUrl: RELAY_A,
status: "running",
});
const currentCommunityFizz = makeAgent({
pubkey: PUB_B,
personaId: WELCOME_GUIDE_PERSONA_ID,
relayUrl: RELAY_B,
status: "stopped",
});
assert.equal(
pickWelcomeGuideAgentForRelay(
[otherCommunityFizz, currentCommunityFizz],
RELAY_B,
),
currentCommunityFizz,
);
});
test("pickWelcomeGuideAgentForRelay returns null when Fizz only exists in another community", () => {
const otherCommunityFizz = makeAgent({
pubkey: PUB_A,
personaId: WELCOME_GUIDE_PERSONA_ID,
relayUrl: RELAY_A,
});
assert.equal(
pickWelcomeGuideAgentForRelay([otherCommunityFizz], RELAY_B),
null,
);
});
test("starter persona activation is serialized to protect the shared store", async () => {
const calls = [];
let activeWrites = 0;
await activateWelcomeTeamPersonasSequentially(
["builtin:fizz", "builtin:honey", "builtin:bumble"],
async (personaId) => {
assert.equal(activeWrites, 0, "activation writes must never overlap");
activeWrites += 1;
calls.push(personaId);
await new Promise((resolve) => setTimeout(resolve, 1));
activeWrites -= 1;
},
);
assert.deepEqual(calls, ["builtin:fizz", "builtin:honey", "builtin:bumble"]);
});
test("all Welcome starters use the onboarding runtime preference", async () => {
const claude = {
id: "claude",
label: "Claude",
avatarUrl: "https://runtime/claude.png",
availability: "available",
command: "claude-code-acp",
binaryPath: "/bin/claude-code-acp",
defaultArgs: [],
mcpCommand: null,
installHint: "",
installInstructionsUrl: "",
canAutoInstall: false,
underlyingCliPath: "/bin/claude",
};
const buzzAgent = {
...claude,
id: "buzz-agent",
label: "Buzz Agent",
command: "buzz-agent",
};
for (const starter of WELCOME_TEAM_STARTERS) {
const input = await buildWelcomeStarterCreateInput(
starter,
{
id: starter.personaId,
displayName: starter.name,
systemPrompt: `${starter.name} prompt`,
model: null,
provider: null,
runtime: null,
avatarUrl: null,
envVars: {},
isBuiltIn: true,
isActive: true,
},
[buzzAgent, claude],
"claude",
RELAY_A,
);
assert.equal(input.agentCommand, "claude-code-acp");
assert.equal(input.harnessOverride, true);
assert.equal(input.personaId, starter.personaId);
assert.equal(input.teamId, WELCOME_TEAM_ID);
assert.equal(input.relayUrl, RELAY_A);
assert.equal(input.spawnAfterCreate, false);
assert.equal(input.startOnAppLaunch, false);
}
});
test("existing Welcome starter rematerializes runtime-specific fields atomically", () => {
const existing = makeAgent({
pubkey: PUB_A,
personaId: WELCOME_GUIDE_PERSONA_ID,
agentCommand: "claude-agent-acp",
agentCommandOverride: "claude-agent-acp",
agentArgs: ["--old"],
mcpCommand: "",
model: "claude-sonnet",
provider: "anthropic",
});
assert.deepEqual(
welcomeStarterRuntimeUpdate(existing, {
name: "Fizz",
agentCommand: "codex-acp",
agentArgs: ["--new"],
mcpCommand: "buzz-dev-mcp",
model: "gpt-5.6-sol",
provider: null,
}),
{
pubkey: PUB_A,
agentCommand: "codex-acp",
harnessOverride: true,
agentArgs: ["--new"],
mcpCommand: "buzz-dev-mcp",
model: "gpt-5.6-sol",
provider: null,
},
);
});
test("existing Welcome starter clears stale model and provider for Claude", () => {
const existing = makeAgent({
personaId: WELCOME_GUIDE_PERSONA_ID,
agentCommand: "codex-acp",
agentArgs: [],
model: "gpt-5.6-sol",
provider: "openai",
});
assert.deepEqual(
welcomeStarterRuntimeUpdate(existing, {
name: "Fizz",
agentCommand: "claude-agent-acp",
agentArgs: [],
mcpCommand: "",
}),
{
pubkey: PUB_A,
agentCommand: "claude-agent-acp",
harnessOverride: true,
agentArgs: [],
mcpCommand: "",
model: null,
provider: null,
},
);
});
test("existing Welcome starter needs no update when runtime already matches", () => {
const existing = makeAgent({
personaId: WELCOME_GUIDE_PERSONA_ID,
agentCommand: "codex-acp",
agentArgs: ["--same"],
});
assert.equal(
welcomeStarterRuntimeUpdate(existing, {
name: "Fizz",
agentCommand: "codex-acp",
agentArgs: ["--same"],
mcpCommand: "buzz-dev-mcp",
model: null,
provider: null,
}),
null,
);
});
test("welcome team starter definitions and role identities are stable", () => {
assert.equal(WELCOME_TEAM_ID, "builtin-team:welcome");
assert.deepEqual(WELCOME_TEAM_STARTERS, [
{ name: "Fizz", personaId: "builtin:fizz", role: "lead" },
{ name: "Honey", personaId: "builtin:honey", role: "teammate" },
{ name: "Bumble", personaId: "builtin:bumble", role: "teammate" },
]);
});
test("starter matching ignores user agents with a Welcome persona", () => {
const honey = WELCOME_TEAM_STARTERS[1];
const userHoney = makeAgent({
personaId: honey.personaId,
teamId: null,
});
assert.equal(
pickWelcomeTeamStarterAgentForRelay([userHoney], honey, RELAY_A),
null,
);
});
test("starter matching uses persona identity rather than display name", () => {
const honey = WELCOME_TEAM_STARTERS[1];
const renamedHoney = makeAgent({
name: "Honey the Helper",
personaId: honey.personaId,
});
const nameOnlyHoney = makeAgent({ name: honey.name, pubkey: PUB_B });
assert.equal(
pickWelcomeTeamStarterAgentForRelay(
[nameOnlyHoney, renamedHoney],
honey,
RELAY_A,
),
renamedHoney,
);
});
test("starter matching is relay scoped and normalizes trailing slashes", () => {
const bumble = WELCOME_TEAM_STARTERS[2];
const otherRelay = makeAgent({
personaId: bumble.personaId,
relayUrl: RELAY_B,
status: "running",
});
const matchingRelay = makeAgent({
personaId: bumble.personaId,
relayUrl: `${RELAY_A}/`,
pubkey: PUB_B,
});
assert.equal(
pickWelcomeTeamStarterAgentForRelay(
[otherRelay, matchingRelay],
bumble,
RELAY_A,
),
matchingRelay,
);
});
test("starter matching prefers running, then deployed instances", () => {
const fizz = WELCOME_TEAM_STARTERS[0];
const stopped = makeAgent({ personaId: fizz.personaId });
const deployed = makeAgent({
personaId: fizz.personaId,
pubkey: PUB_B,
status: "deployed",
});
const running = makeAgent({
personaId: fizz.personaId,
pubkey: PUB_C,
status: "running",
});
assert.equal(
pickWelcomeTeamStarterAgentForRelay(
[stopped, deployed, running],
fizz,
RELAY_A,
),
running,
);
assert.equal(
pickWelcomeTeamStarterAgentForRelay([stopped, deployed], fizz, RELAY_A),
deployed,
);
});
test("owner-only-access policy accepts local Welcome teammates", () => {
const teammate = makeAgent({
respondTo: "owner-only",
respondToAllowlist: [],
});
assert.equal(welcomeTeammateHasExpectedAccess(teammate, PUB_B, true), true);
assert.equal(welcomeTeammateHasExpectedAccess(teammate, PUB_B, false), false);
});
test("access remediation converges for an upgraded owner-only install", () => {
// Pre-existing installs allowlisted the lead. An owner-only build must move
// them to owner-only, and the write it makes must satisfy the predicate, so
// the next provisioning pass makes no further write.
const allowlisted = makeAgent({
respondTo: "allowlist",
respondToAllowlist: [PUB_B],
});
const update = welcomeTeammateAccessUpdate(allowlisted, PUB_B, true);
assert.deepEqual(update, {
pubkey: PUB_A,
respondTo: "owner-only",
respondToAllowlist: [],
});
const remediated = makeAgent({
respondTo: update.respondTo,
respondToAllowlist: update.respondToAllowlist,
});
assert.equal(welcomeTeammateHasExpectedAccess(remediated, PUB_B, true), true);
assert.equal(welcomeTeammateAccessUpdate(remediated, PUB_B, true), null);
});
test("access remediation allowlists the lead when the build is not owner-only", () => {
const ownerOnly = makeAgent({
respondTo: "owner-only",
respondToAllowlist: [],
});
const update = welcomeTeammateAccessUpdate(ownerOnly, PUB_B, false);
assert.deepEqual(update, {
pubkey: PUB_A,
respondTo: "allowlist",
respondToAllowlist: [PUB_B],
});
const remediated = makeAgent({
respondTo: update.respondTo,
respondToAllowlist: update.respondToAllowlist,
});
assert.equal(
welcomeTeammateHasExpectedAccess(remediated, PUB_B, false),
true,
);
assert.equal(welcomeTeammateAccessUpdate(remediated, PUB_B, false), null);
});
test("access remediation skips a teammate that already allows the lead", () => {
const allowlisted = makeAgent({
respondTo: "allowlist",
respondToAllowlist: [PUB_B, PUB_C],
});
assert.equal(welcomeTeammateAccessUpdate(allowlisted, PUB_B, false), null);
});
test("owner-only-access policy accepts provider Welcome teammates", () => {
const teammate = makeAgent({
backend: { type: "provider", id: "remote", config: {} },
respondTo: "owner-only",
respondToAllowlist: [],
});
assert.equal(welcomeTeammateHasExpectedAccess(teammate, PUB_B, true), true);
assert.equal(welcomeTeammateHasExpectedAccess(teammate, PUB_B, false), false);
});