diff --git a/desktop/playwright.config.ts b/desktop/playwright.config.ts
index c571472a36b..1e497360359 100644
--- a/desktop/playwright.config.ts
+++ b/desktop/playwright.config.ts
@@ -78,6 +78,7 @@ export default defineConfig({
"**/composer-tooltip-dismiss.spec.ts",
"**/mentions.spec.ts",
"**/mention-spacing.spec.ts",
+ "**/cloud-provenance.spec.ts",
"**/team-mentions.spec.ts",
"**/persistent-agent-audience.spec.ts",
"**/relay-reconnect.spec.ts",
diff --git a/desktop/src/features/agents/AGENTS.md b/desktop/src/features/agents/AGENTS.md
index 274760725f7..ff8df71cd30 100644
--- a/desktop/src/features/agents/AGENTS.md
+++ b/desktop/src/features/agents/AGENTS.md
@@ -216,6 +216,11 @@ with a TypeScript lookup table or an id comparison in a component.
Unqueried persona siblings are unknown. No presence state grants deletion or
Stop authority; native local stop-before-remove remains independent. See
[the availability contract](../../../../docs/agent-availability.md).
+ The shared cloud marker means “Not managed on this device” only
+ after ownership and successful local inventory are known. It does not imply
+ hosting location, availability, or permission. Keep all identity surfaces on
+ the shared provenance context, without per-row directory subscriptions. See
+ [the provenance contract](../../../../docs/agent-management-provenance.md).
14. **Thinking effort has two surfaces: a local-only WRITE control and a
read-only two-facts DISPLAY.** The write control is `EffortPickerField`
(`ui/EffortPickerField.tsx`), a self-contained section component mounted in
diff --git a/desktop/src/features/agents/lib/otherSetupAgent.test.mjs b/desktop/src/features/agents/lib/otherSetupAgent.test.mjs
index f57c7f8154f..a45ddb5e64c 100644
--- a/desktop/src/features/agents/lib/otherSetupAgent.test.mjs
+++ b/desktop/src/features/agents/lib/otherSetupAgent.test.mjs
@@ -1,7 +1,10 @@
import assert from "node:assert/strict";
import test from "node:test";
-import { isOtherSetupAgent } from "./otherSetupAgent.ts";
+import {
+ isOtherSetupAgent,
+ isOwnedAgentNotManagedOnDevice,
+} from "./otherSetupAgent.ts";
const OWNER = "a".repeat(64);
const AGENT = "b".repeat(64);
@@ -20,7 +23,7 @@ test("fails closed while the local managed directory is unresolved", () => {
);
});
-test("labels a viewer-owned non-local identity as another setup", () => {
+test("labels a viewer-owned identity as not managed on this device", () => {
assert.equal(
isOtherSetupAgent({
agentDirectoriesReady: true,
@@ -33,3 +36,38 @@ test("labels a viewer-owned non-local identity as another setup", () => {
true,
);
});
+
+test("a locally managed provider is not labeled as another device", () => {
+ assert.equal(
+ isOtherSetupAgent({
+ agentDirectoriesReady: true,
+ currentPubkey: OWNER,
+ managedAgents: [{ pubkey: AGENT, backend: { type: "provider" } }],
+ profileOwnerPubkey: OWNER,
+ pubkey: AGENT,
+ relayAgents: [],
+ }),
+ false,
+ );
+});
+
+for (const [name, overrides, expected] of [
+ ["owned absent key", {}, true],
+ ["loading local inventory", { localInventoryReady: false }, false],
+ ["exact local provider record", { isLocallyManaged: true }, false],
+ ["different owner", { ownerPubkey: "b".repeat(64) }, false],
+ ["unknown ownership", { ownerPubkey: null }, false],
+]) {
+ test(`shared provenance: ${name}`, () => {
+ assert.equal(
+ isOwnedAgentNotManagedOnDevice({
+ currentPubkey: "a".repeat(64),
+ ownerPubkey: "A".repeat(64),
+ localInventoryReady: true,
+ isLocallyManaged: false,
+ ...overrides,
+ }),
+ expected,
+ );
+ });
+}
diff --git a/desktop/src/features/agents/lib/otherSetupAgent.ts b/desktop/src/features/agents/lib/otherSetupAgent.ts
index 63438a983fd..f94215e1f3a 100644
--- a/desktop/src/features/agents/lib/otherSetupAgent.ts
+++ b/desktop/src/features/agents/lib/otherSetupAgent.ts
@@ -1,6 +1,7 @@
import type { ManagedAgent, RelayAgent } from "@/shared/api/types";
import { normalizePubkey } from "@/shared/lib/pubkey";
+/** Owned identity absent from the loaded local inventory; not evidence of hosting location. */
export function isOtherSetupAgent({
agentDirectoriesReady,
currentPubkey,
@@ -32,8 +33,31 @@ export function isOtherSetupAgent({
)?.ownerPubkey;
const ownerPubkey = profileOwnerPubkey ?? relayOwnerPubkey;
+ return isOwnedAgentNotManagedOnDevice({
+ currentPubkey,
+ ownerPubkey,
+ localInventoryReady: agentDirectoriesReady,
+ isLocallyManaged: false,
+ });
+}
+
+/** Presentation provenance only; neither hosting location nor availability. */
+export function isOwnedAgentNotManagedOnDevice({
+ currentPubkey,
+ ownerPubkey,
+ localInventoryReady,
+ isLocallyManaged,
+}: {
+ currentPubkey?: string;
+ ownerPubkey?: string | null;
+ localInventoryReady: boolean;
+ isLocallyManaged: boolean;
+}): boolean {
return Boolean(
- ownerPubkey &&
+ localInventoryReady &&
+ !isLocallyManaged &&
+ currentPubkey &&
+ ownerPubkey &&
normalizePubkey(ownerPubkey) === normalizePubkey(currentPubkey),
);
}
diff --git a/desktop/src/features/agents/ui/OtherSetupAgentMarker.tsx b/desktop/src/features/agents/ui/OtherSetupAgentMarker.tsx
index 19ef31bd75b..11b7eeed3b0 100644
--- a/desktop/src/features/agents/ui/OtherSetupAgentMarker.tsx
+++ b/desktop/src/features/agents/ui/OtherSetupAgentMarker.tsx
@@ -1,8 +1,10 @@
import { Cloud } from "lucide-react";
+import { useIsOtherSetupAgent } from "../useKnownAgentPubkeys";
+
import { cn } from "@/shared/lib/cn";
-const OTHER_SETUP_LABEL = "From another Buzz setup";
+const OTHER_SETUP_LABEL = "Not managed on this device";
export function OtherSetupAgentMarker({
className,
@@ -23,3 +25,21 @@ export function OtherSetupAgentMarker({
);
}
+
+/** Connected marker for identity details; shares the app's directory subscriptions. */
+export function AgentManagementMarker({
+ pubkey,
+ ownerPubkey,
+ className,
+ testId,
+}: {
+ pubkey?: string | null;
+ ownerPubkey?: string | null;
+ className?: string;
+ testId?: string;
+}) {
+ const show = useIsOtherSetupAgent(pubkey, ownerPubkey);
+ return show ? (
+