feat(permissions): expose network permission state (#103)

This commit is contained in:
2026-07-25 23:05:55 +02:00
parent e8731834f4
commit 3047dc9195
31 changed files with 2080 additions and 94 deletions

View File

@ -40,7 +40,10 @@ import type {
ProjectMcpToolPermissions,
ProjectPermissions,
ProjectWorkState,
ProjectSystemPermissions,
ProfileAvailability,
ResolvedAgentSystemPermissions,
SystemPermissionSet,
Skill,
SkillScope,
Template,
@ -364,6 +367,37 @@ export class HttpPermissionGateway implements PermissionGateway {
request: { projectId, agentId },
});
}
getProjectSystemPermissions(projectId: string): Promise<ProjectSystemPermissions> {
return this.http.invoke<ProjectSystemPermissions>("get_project_system_permissions", {
projectId,
});
}
updateProjectSystemPermissions(
projectId: string,
permissions: SystemPermissionSet | null,
): Promise<ProjectSystemPermissions> {
return this.http.invoke<ProjectSystemPermissions>("update_project_system_permissions", {
request: { projectId, permissions },
});
}
updateAgentSystemPermissions(
projectId: string,
agentId: string,
permissions: SystemPermissionSet | null,
): Promise<ProjectSystemPermissions> {
return this.http.invoke<ProjectSystemPermissions>("update_agent_system_permissions", {
request: { projectId, agentId, permissions },
});
}
resolveAgentSystemPermissions(
projectId: string,
agentId: string,
): Promise<ResolvedAgentSystemPermissions> {
return this.http.invoke<ResolvedAgentSystemPermissions>(
"resolve_agent_system_permissions",
{ request: { projectId, agentId } },
);
}
getMcpToolPermissions(projectId: string): Promise<ProjectMcpToolPermissions> {
return this.http.invoke<ProjectMcpToolPermissions>("get_mcp_tool_permissions", { projectId });
}

View File

@ -50,8 +50,10 @@ import type {
ProjectMcpToolPermissions,
ProjectPermissions,
ProjectWorkState,
ProjectSystemPermissions,
ProfileAvailability,
ResumableAgent,
ResolvedAgentSystemPermissions,
ServerExposurePreview,
ServerExposureSettings,
Skill,
@ -59,6 +61,7 @@ import type {
SkillScope,
Sprint,
Template,
SystemPermissionSet,
TerminalSession,
Ticket,
TicketCarnet,
@ -2186,6 +2189,8 @@ export class MockDeviceGateway implements DeviceGateway {
/** In-memory permissions gateway. */
export class MockPermissionGateway implements PermissionGateway {
private docs = new Map<string, ProjectPermissions>();
private systemDocs = new Map<string, ProjectSystemPermissions>();
private systemRuntime = new Map<string, ResolvedAgentSystemPermissions>();
private doc(projectId: string): ProjectPermissions {
if (!this.docs.has(projectId)) {
@ -2234,6 +2239,72 @@ export class MockPermissionGateway implements PermissionGateway {
};
}
private systemDoc(projectId: string): ProjectSystemPermissions {
if (!this.systemDocs.has(projectId)) {
this.systemDocs.set(projectId, { version: 1, agents: [] });
}
return this.systemDocs.get(projectId)!;
}
async getProjectSystemPermissions(
projectId: string,
): Promise<ProjectSystemPermissions> {
return structuredClone(this.systemDoc(projectId));
}
async updateProjectSystemPermissions(
projectId: string,
permissions: SystemPermissionSet | null,
): Promise<ProjectSystemPermissions> {
const doc = this.systemDoc(projectId);
if (permissions && Object.keys(permissions).length > 0) {
doc.projectDefault = structuredClone(permissions);
} else {
delete doc.projectDefault;
}
return structuredClone(doc);
}
async updateAgentSystemPermissions(
projectId: string,
agentId: string,
permissions: SystemPermissionSet | null,
): Promise<ProjectSystemPermissions> {
const doc = this.systemDoc(projectId);
const agents = (doc.agents ?? []).filter((entry) => entry.agentId !== agentId);
if (permissions && Object.keys(permissions).length > 0) {
agents.push({ agentId, permissions: structuredClone(permissions) });
}
doc.agents = agents;
return structuredClone(doc);
}
async resolveAgentSystemPermissions(
projectId: string,
agentId: string,
): Promise<ResolvedAgentSystemPermissions> {
const runtime = this.systemRuntime.get(agentId);
if (runtime) return structuredClone(runtime);
const doc = this.systemDoc(projectId);
const wanted =
doc.agents?.find((entry) => entry.agentId === agentId)?.permissions.network ??
doc.projectDefault?.network ??
null;
return {
wanted,
effective: wanted ?? "ask",
runtimeLock: { state: "none" },
control: { mode: "editable" },
};
}
setResolvedAgentSystemPermissions(
agentId: string,
resolved: ResolvedAgentSystemPermissions,
): void {
this.systemRuntime.set(agentId, structuredClone(resolved));
}
// ── MCP tool permissions (ticket #82) — mirrors the backend catalogue in
// `crates/infrastructure/src/orchestrator/mcp/tools.rs`, a separate durable
// document from the file/command permissions above. ─────────────────────

View File

@ -6,6 +6,9 @@ import type {
PermissionSet,
ProjectMcpToolPermissions,
ProjectPermissions,
ProjectSystemPermissions,
ResolvedAgentSystemPermissions,
SystemPermissionSet,
} from "@/domain";
import type { PermissionGateway } from "@/ports";
@ -43,6 +46,41 @@ export class TauriPermissionGateway implements PermissionGateway {
});
}
getProjectSystemPermissions(projectId: string): Promise<ProjectSystemPermissions> {
return invoke<ProjectSystemPermissions>("get_project_system_permissions", {
projectId,
});
}
updateProjectSystemPermissions(
projectId: string,
permissions: SystemPermissionSet | null,
): Promise<ProjectSystemPermissions> {
return invoke<ProjectSystemPermissions>("update_project_system_permissions", {
request: { projectId, permissions },
});
}
updateAgentSystemPermissions(
projectId: string,
agentId: string,
permissions: SystemPermissionSet | null,
): Promise<ProjectSystemPermissions> {
return invoke<ProjectSystemPermissions>("update_agent_system_permissions", {
request: { projectId, agentId, permissions },
});
}
resolveAgentSystemPermissions(
projectId: string,
agentId: string,
): Promise<ResolvedAgentSystemPermissions> {
return invoke<ResolvedAgentSystemPermissions>(
"resolve_agent_system_permissions",
{ request: { projectId, agentId } },
);
}
getMcpToolPermissions(projectId: string): Promise<ProjectMcpToolPermissions> {
return invoke<ProjectMcpToolPermissions>("get_mcp_tool_permissions", {
projectId,