Files
multica/packages/core/agents/effective-access.ts
chouti b31132edf3 feat(agents): add access-scope column, filter, and bulk edit to agents list (#5393)
Adds a three-state access column/filter and a bulk "Set access scope" action to the agents list, plus a fix for a state-update loop in the bulk access dialog (parent/child callback + effect cleanup were re-triggering each other).

Co-authored-by: chouti <chouti@users.noreply.github.com>
2026-07-15 15:53:24 +08:00

57 lines
2.3 KiB
TypeScript

import type { AgentInvocationTarget, AgentPermissionMode } from "../types";
/**
* The three effective access-scope states shown in the agents list, derived
* from the authoritative `permission_mode` + `invocation_targets` (MUL-3963).
* The legacy `visibility` field is a lossy two-state projection of these — a
* `public_to` agent scoped to specific people maps to `visibility: "private"`,
* indistinguishable from a truly owner-only agent — so the list shows this
* three-state value instead.
*
* Mapping mirrors the server's `canInvokeAgent` gate
* (`server/internal/handler/agent_access.go`):
* - owner-only = `private` (only the owner may invoke)
* - workspace = `public_to` with a workspace target (any member/agent/system)
* - specific-people = `public_to` without a workspace target (member/team targets)
*/
export type AccessScope = "workspace" | "specific-people" | "owner-only";
/**
* Derive the effective access scope from an agent's permission fields. Fails
* safe to "owner-only" when `permission_mode` is absent (the runtime may omit
* these on legacy self-host backends or stale caches); a `public_to` agent
* with absent `invocation_targets` stays "specific-people".
*/
export function effectiveAccessScope(
permissionMode: AgentPermissionMode | undefined | null,
invocationTargets: readonly AgentInvocationTarget[] | undefined | null,
): AccessScope {
if (permissionMode !== "public_to") {
return "owner-only";
}
if ((invocationTargets ?? []).some((t) => t.target_type === "workspace")) {
return "workspace";
}
return "specific-people";
}
/** All possible effective access-scope values, in display order. */
export const ALL_ACCESS_SCOPES: readonly AccessScope[] = [
"workspace",
"specific-people",
"owner-only",
];
/**
* Whether the bulk-access dialog's Apply button should be enabled given the
* picker's current onChange payload. `null` (no selection yet) and
* `public_to` with zero invocation targets are not ready.
*/
export function isAccessChangeReady(
change: { permission_mode: AgentPermissionMode; invocation_targets: readonly { target_type: string }[] } | null,
): boolean {
if (!change) return false;
if (change.permission_mode === "private") return true;
return change.invocation_targets.length > 0;
}