diff --git a/AgentCompany-New/src/main/app/ipc-handlers.ts b/AgentCompany-New/src/main/app/ipc-handlers.ts index 98401bcc..ceaedd62 100644 --- a/AgentCompany-New/src/main/app/ipc-handlers.ts +++ b/AgentCompany-New/src/main/app/ipc-handlers.ts @@ -86,6 +86,7 @@ export function registerIpcHandlers(deps: IpcDeps): void { ipcMain.handle(IPC_CHANNELS.setActiveOrganization, (_event, input) => mutate(() => organization.setActiveOrganization(input.orgId))); ipcMain.handle(IPC_CHANNELS.deleteOrganization, (_event, input) => mutate(() => organization.deleteOrganization(input.orgId))); ipcMain.handle(IPC_CHANNELS.createAgent, (_event, input) => mutate(() => organization.createAgent(input))); + ipcMain.handle(IPC_CHANNELS.updateAgent, (_event, input) => mutate(() => organization.updateAgent(input))); ipcMain.handle(IPC_CHANNELS.createTask, (_event, input) => mutate(() => organization.createTask(input))); ipcMain.handle(IPC_CHANNELS.updateTaskStatus, (_event, input) => mutate(() => organization.updateTaskStatus(input.orgId, input.taskId, input.status))); ipcMain.handle(IPC_CHANNELS.runTask, async (_event, input) => { diff --git a/AgentCompany-New/src/main/services/organization-service.ts b/AgentCompany-New/src/main/services/organization-service.ts index 69d64f5b..83ebf5d0 100644 --- a/AgentCompany-New/src/main/services/organization-service.ts +++ b/AgentCompany-New/src/main/services/organization-service.ts @@ -2,7 +2,7 @@ import type { DatabaseSync } from "node:sqlite"; import { APPROVAL_STATE, RISK_LEVEL, TASK_STATUS } from "../../shared/domain"; import { PROVIDER_CATALOG } from "../../shared/provider-catalog"; import { createId } from "../../shared/ids"; -import type { CreateAgentInput, CreateOrganizationInput, CreateTaskInput } from "../../shared/commands"; +import type { CreateAgentInput, CreateOrganizationInput, CreateTaskInput, UpdateAgentInput } from "../../shared/commands"; import { AuditService } from "./audit-service"; import { PolicyService } from "./policy-service"; import { IdentityAccessService } from "./identity-access-service"; @@ -213,6 +213,34 @@ export class OrganizationService { return id; } + updateAgent(input: UpdateAgentInput): void { + const agent = this.db.prepare("select id from agents where id = ? and org_id = ?") + .get(input.agentId, input.orgId) as { id: string } | undefined; + if (!agent) throw new Error("Agent was not found."); + const role = input.role.trim(); + if (!role) throw new Error("Agent role is required."); + if (input.modelProviderId) { + const provider = this.db.prepare("select id from providers where id = ? and org_id = ?") + .get(input.modelProviderId, input.orgId) as { id: string } | undefined; + if (!provider) throw new Error("Agent provider was not found."); + } + const result = this.db.prepare(` + update agents + set role = ?, title = ?, role_level = ?, model_provider_id = ?, model_id = ? + where id = ? and org_id = ? + `).run(role, input.title.trim() || role, input.roleLevel.trim() || "staff", input.modelProviderId, input.modelId, input.agentId, input.orgId); + if (result.changes === 0) throw new Error("Agent update failed."); + this.audit.record({ + orgId: input.orgId, + entityType: "agent", + entityId: input.agentId, + actorType: "employee", + actorId: null, + action: "updated", + payload: { role, roleLevel: input.roleLevel.trim() || "staff" }, + }); + } + createTask(input: CreateTaskInput): string { const id = createId("tsk"); const now = new Date().toISOString(); diff --git a/AgentCompany-New/src/preload/index.ts b/AgentCompany-New/src/preload/index.ts index 60df5c72..adf66cf5 100644 --- a/AgentCompany-New/src/preload/index.ts +++ b/AgentCompany-New/src/preload/index.ts @@ -13,6 +13,7 @@ const api: DesktopApi = { setActiveOrganization: (input) => invoke(IPC_CHANNELS.setActiveOrganization, input), deleteOrganization: (input) => invoke(IPC_CHANNELS.deleteOrganization, input), createAgent: (input) => invoke(IPC_CHANNELS.createAgent, input), + updateAgent: (input) => invoke(IPC_CHANNELS.updateAgent, input), createTask: (input) => invoke(IPC_CHANNELS.createTask, input), updateTaskStatus: (input) => invoke(IPC_CHANNELS.updateTaskStatus, input), runTask: (input) => invoke(IPC_CHANNELS.runTask, input), diff --git a/AgentCompany-New/src/renderer/src/pages/TeamPage.tsx b/AgentCompany-New/src/renderer/src/pages/TeamPage.tsx index e9315cf8..824010c3 100644 --- a/AgentCompany-New/src/renderer/src/pages/TeamPage.tsx +++ b/AgentCompany-New/src/renderer/src/pages/TeamPage.tsx @@ -17,6 +17,7 @@ function optional(form: FormData, key: string): string | null { export function TeamPage({ snapshot, actions }: PageProps) { const orgId = activeOrgId(snapshot); const [hireOpen, setHireOpen] = useState(false); + const [editAgentId, setEditAgentId] = useState(null); const [filter, setFilter] = useState<"all" | "human" | "agent">("all"); const employees = useMemo(() => snapshot.employees.filter((item) => item.orgId === orgId), [snapshot.employees, orgId]); @@ -46,6 +47,23 @@ export function TeamPage({ snapshot, actions }: PageProps) { })).then((result) => { if (result) setHireOpen(false); }); }; + const editingAgent = editAgentId ? agents.find((agent) => agent.id === editAgentId) ?? null : null; + + const submitEdit = (event: FormEvent) => { + event.preventDefault(); + if (!editingAgent) return; + const form = new FormData(event.currentTarget); + void actions.run(() => window.agentCompany.updateAgent({ + orgId, + agentId: editingAgent.id, + role: text(form, "role"), + title: text(form, "title") || text(form, "role"), + roleLevel: text(form, "roleLevel") || "staff", + modelProviderId: optional(form, "modelProviderId"), + modelId: optional(form, "modelId"), + })).then((result) => { if (result) setEditAgentId(null); }); + }; + const rosterRows = buildRoster(employees, agents, providers, filter); return ( @@ -95,7 +113,12 @@ export function TeamPage({ snapshot, actions }: PageProps) {
Member
Department
Model
Budget
{rosterRows.map((row, index) => ( - + setEditAgentId(row.id)} + /> ))} @@ -111,19 +134,8 @@ export function TeamPage({ snapshot, actions }: PageProps) { - + - - {snapshot.providerCatalog.flatMap((entry) => - entry.models.map((model) => ( - - )), - )} - + + {editingAgent && ( + setEditAgentId(null)} onSubmit={submitEdit}> + + + + + + + + + + + + + )} + + + {snapshot.providerCatalog.flatMap((entry) => + entry.models.map((model) => ( + + )), + )} + ); } @@ -244,7 +288,7 @@ function buildRoster( return rows; } -function RosterRow({ row, last }: { row: RosterEntry; last: boolean }) { +function RosterRow({ row, last, onEdit }: { row: RosterEntry; last: boolean; onEdit: (() => void) | null }) { return (
- + {onEdit && }
); diff --git a/AgentCompany-New/src/shared/commands.ts b/AgentCompany-New/src/shared/commands.ts index f5c0523f..7a331f66 100644 --- a/AgentCompany-New/src/shared/commands.ts +++ b/AgentCompany-New/src/shared/commands.ts @@ -36,6 +36,16 @@ export interface CreateOrganizationInput { reviewDeliverables?: boolean; } +export interface UpdateAgentInput { + orgId: string; + agentId: string; + role: string; + title: string; + roleLevel: string; + modelProviderId: string | null; + modelId: string | null; +} + export interface CreateAgentInput { orgId: string; name: string; diff --git a/AgentCompany-New/src/shared/desktop-api.ts b/AgentCompany-New/src/shared/desktop-api.ts index 16b5c3f3..e31f23f6 100644 --- a/AgentCompany-New/src/shared/desktop-api.ts +++ b/AgentCompany-New/src/shared/desktop-api.ts @@ -6,6 +6,7 @@ import type { BootstrapOnboardingSummary, CancelProviderResponseInput, CreateAgentInput, + UpdateAgentInput, CreateApprovalInput, CreateAuditReplayInput, CreateChatSpaceInput, @@ -68,6 +69,7 @@ export interface DesktopApi { deleteOrganization(input: DeleteOrganizationInput): Promise>; createOrganization(input: CreateOrganizationInput): Promise>; createAgent(input: CreateAgentInput): Promise>; + updateAgent(input: UpdateAgentInput): Promise>; createTask(input: CreateTaskInput): Promise>; updateTaskStatus(input: UpdateTaskStatusInput): Promise>; runTask(input: RunTaskInput): Promise>; diff --git a/AgentCompany-New/src/shared/ipc.ts b/AgentCompany-New/src/shared/ipc.ts index a5799a66..cbd1a089 100644 --- a/AgentCompany-New/src/shared/ipc.ts +++ b/AgentCompany-New/src/shared/ipc.ts @@ -5,6 +5,7 @@ export const IPC_CHANNELS = { deleteOrganization: "agentcompany:delete-organization", createOrganization: "agentcompany:create-organization", createAgent: "agentcompany:create-agent", + updateAgent: "agentcompany:update-agent", createTask: "agentcompany:create-task", updateTaskStatus: "agentcompany:update-task-status", runTask: "agentcompany:run-task",