fix(04): wire pending approval notifications in IPC and API server paths
This commit is contained in:
1 parent
9d01a7acc4
commit
a2a110f0c5
5 files changed
+26
-2
No files matched your search
+11
-1
@@ -9,7 +9,7 @@ import { resolveApprovalDecision, shouldAutoApproveApproval } from "./approval-p
|
|||||||
import type { AppDatabase } from "./database";
|
import type { AppDatabase } from "./database";
|
||||||
import { inferTaskType } from "./connector-matching";
|
import { inferTaskType } from "./connector-matching";
|
||||||
import { dispatchAgentMessage } from "./message-dispatch";
|
import { dispatchAgentMessage } from "./message-dispatch";
|
||||||
import type { DesktopEvent } from "@shared/types";
|
import type { DesktopEvent, SectionId } from "@shared/types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode an HTTP request body to a UTF-8 string, handling the common Windows
|
* Decode an HTTP request body to a UTF-8 string, handling the common Windows
|
||||||
@@ -78,6 +78,7 @@ export interface ApiServerEventCallbacks {
|
|||||||
onDocumentCreated: (companyId: string, authorAgentId: string | null, projectId: string | null) => void;
|
onDocumentCreated: (companyId: string, authorAgentId: string | null, projectId: string | null) => void;
|
||||||
onAutoAssignTask: (taskId: string, companyId: string) => string | null;
|
onAutoAssignTask: (taskId: string, companyId: string) => string | null;
|
||||||
emitEvent: (event: DesktopEvent) => void;
|
emitEvent: (event: DesktopEvent) => void;
|
||||||
|
notify: (options: { title: string; body: string; urgency: "critical" | "informational"; navigation?: { section: SectionId; entityId?: string }; batchKey?: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AgentApiServer {
|
export class AgentApiServer {
|
||||||
@@ -114,6 +115,7 @@ export class AgentApiServer {
|
|||||||
onDocumentCreated: callbacks?.onDocumentCreated ?? (() => {}),
|
onDocumentCreated: callbacks?.onDocumentCreated ?? (() => {}),
|
||||||
onAutoAssignTask: callbacks?.onAutoAssignTask ?? (() => null),
|
onAutoAssignTask: callbacks?.onAutoAssignTask ?? (() => null),
|
||||||
emitEvent: callbacks?.emitEvent ?? (() => {}),
|
emitEvent: callbacks?.emitEvent ?? (() => {}),
|
||||||
|
notify: callbacks?.notify ?? (() => {}),
|
||||||
};
|
};
|
||||||
this.registerRoutes();
|
this.registerRoutes();
|
||||||
}
|
}
|
||||||
@@ -1009,6 +1011,14 @@ export class AgentApiServer {
|
|||||||
detail: "Auto-approved by company policy",
|
detail: "Auto-approved by company policy",
|
||||||
});
|
});
|
||||||
log.info(`[auto-approve] Auto-approved approval ${id}: ${input.payloadSummary}`);
|
log.info(`[auto-approve] Auto-approved approval ${id}: ${input.payloadSummary}`);
|
||||||
|
} else {
|
||||||
|
this.callbacks.notify({
|
||||||
|
title: "Approval Required",
|
||||||
|
body: input.payloadSummary ?? `${input.type} approval pending`,
|
||||||
|
urgency: "critical",
|
||||||
|
navigation: { section: "approvals", entityId: id },
|
||||||
|
batchKey: "pending_approval",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onDomainChanged();
|
this.onDomainChanged();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { createServiceGraph } from "./service-graph";
|
|||||||
import { createIpcSupport } from "./ipc-support";
|
import { createIpcSupport } from "./ipc-support";
|
||||||
import { createNotificationService } from "./notification-service";
|
import { createNotificationService } from "./notification-service";
|
||||||
import type { NotificationOptions } from "./notification-service";
|
import type { NotificationOptions } from "./notification-service";
|
||||||
import type { DesktopEvent } from "@shared/types";
|
import type { DesktopEvent, SectionId } from "@shared/types";
|
||||||
|
|
||||||
interface BootstrapApplicationDependencies {
|
interface BootstrapApplicationDependencies {
|
||||||
hasSingleInstanceLock: boolean;
|
hasSingleInstanceLock: boolean;
|
||||||
@@ -33,6 +33,7 @@ interface BootstrapApplicationDependencies {
|
|||||||
onDocumentCreated: (companyId: string, authorAgentId: string | null, projectId: string | null) => void;
|
onDocumentCreated: (companyId: string, authorAgentId: string | null, projectId: string | null) => void;
|
||||||
onAutoAssignTask: (taskId: string, companyId: string) => string | null;
|
onAutoAssignTask: (taskId: string, companyId: string) => string | null;
|
||||||
emitEvent: (event: DesktopEvent) => void;
|
emitEvent: (event: DesktopEvent) => void;
|
||||||
|
notify: (options: { title: string; body: string; urgency: "critical" | "informational"; navigation?: { section: SectionId; entityId?: string }; batchKey?: string }) => void;
|
||||||
}, allowedOrigins: string[]) => AgentApiServer;
|
}, allowedOrigins: string[]) => AgentApiServer;
|
||||||
createRunToken: (agentId: string, companyId: string, runId: string) => string;
|
createRunToken: (agentId: string, companyId: string, runId: string) => string;
|
||||||
seedDefaultAutomationRules: (companyId: string) => void;
|
seedDefaultAutomationRules: (companyId: string) => void;
|
||||||
@@ -168,6 +169,7 @@ export async function bootstrapApplication({
|
|||||||
},
|
},
|
||||||
onAutoAssignTask: (taskId, companyId) => db.autoAssignTask(taskId, companyId),
|
onAutoAssignTask: (taskId, companyId) => db.autoAssignTask(taskId, companyId),
|
||||||
emitEvent,
|
emitEvent,
|
||||||
|
notify,
|
||||||
}, [isDev && devServerUrl ? new URL(devServerUrl).origin : null, "app://-"].filter((origin): origin is string => Boolean(origin)));
|
}, [isDev && devServerUrl ? new URL(devServerUrl).origin : null, "app://-"].filter((origin): origin is string => Boolean(origin)));
|
||||||
|
|
||||||
apiServerRef.current = apiServer;
|
apiServerRef.current = apiServer;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { IpcMainInvokeEvent } from "electron";
|
import type { IpcMainInvokeEvent } from "electron";
|
||||||
import type { DesktopResult } from "@shared/contracts";
|
import type { DesktopResult } from "@shared/contracts";
|
||||||
|
import type { SectionId } from "@shared/types";
|
||||||
import type { AppDatabase } from "./database";
|
import type { AppDatabase } from "./database";
|
||||||
|
|
||||||
export type CompanyScopedTable = "workspaces" | "agents" | "goals" | "projects" | "tasks" | "approvals" | "runs" | "secrets";
|
export type CompanyScopedTable = "workspaces" | "agents" | "goals" | "projects" | "tasks" | "approvals" | "runs" | "secrets";
|
||||||
@@ -30,6 +31,7 @@ export interface RegisterCoreHandlersDependencies {
|
|||||||
resetAfterRestore: () => void;
|
resetAfterRestore: () => void;
|
||||||
getApiPort: () => number;
|
getApiPort: () => number;
|
||||||
listConnectorModels: (connectorId: string) => Promise<Array<{ id: string; label: string }>>;
|
listConnectorModels: (connectorId: string) => Promise<Array<{ id: string; label: string }>>;
|
||||||
|
notify: (options: { title: string; body: string; urgency: "critical" | "informational"; navigation?: { section: SectionId; entityId?: string }; batchKey?: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ok<T>(data: T): DesktopResult<T> {
|
export function ok<T>(data: T): DesktopResult<T> {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export function registerCoreOperationHandlers({
|
|||||||
dispatchAutomation,
|
dispatchAutomation,
|
||||||
queueTaskRun,
|
queueTaskRun,
|
||||||
getWorkerProcess,
|
getWorkerProcess,
|
||||||
|
notify,
|
||||||
}: Pick<
|
}: Pick<
|
||||||
RegisterCoreHandlersDependencies,
|
RegisterCoreHandlersDependencies,
|
||||||
| "registerHandle"
|
| "registerHandle"
|
||||||
@@ -37,6 +38,7 @@ export function registerCoreOperationHandlers({
|
|||||||
| "dispatchAutomation"
|
| "dispatchAutomation"
|
||||||
| "queueTaskRun"
|
| "queueTaskRun"
|
||||||
| "getWorkerProcess"
|
| "getWorkerProcess"
|
||||||
|
| "notify"
|
||||||
>) {
|
>) {
|
||||||
registerHandle(IPC_CHANNELS.requestApproval, async (_event, payload) => {
|
registerHandle(IPC_CHANNELS.requestApproval, async (_event, payload) => {
|
||||||
const parsed = approvalInputSchema.parse(payload);
|
const parsed = approvalInputSchema.parse(payload);
|
||||||
@@ -81,6 +83,13 @@ export function registerCoreOperationHandlers({
|
|||||||
agentId: parsed.requestedByAgentId ?? undefined,
|
agentId: parsed.requestedByAgentId ?? undefined,
|
||||||
taskId: parsed.relatedTaskId ?? undefined,
|
taskId: parsed.relatedTaskId ?? undefined,
|
||||||
});
|
});
|
||||||
|
notify({
|
||||||
|
title: "Approval Required",
|
||||||
|
body: parsed.payloadSummary ?? `${parsed.type} approval pending`,
|
||||||
|
urgency: "critical",
|
||||||
|
navigation: { section: "approvals", entityId: id },
|
||||||
|
batchKey: "pending_approval",
|
||||||
|
});
|
||||||
publishDomainChanged();
|
publishDomainChanged();
|
||||||
return ok(id);
|
return ok(id);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ export function createMainRuntimeSupport(state: MainRuntimeState) {
|
|||||||
requireBrowserActionService().resetBrowserManager();
|
requireBrowserActionService().resetBrowserManager();
|
||||||
},
|
},
|
||||||
getApiPort: () => state.apiServer?.getPort() ?? 0,
|
getApiPort: () => state.apiServer?.getPort() ?? 0,
|
||||||
|
notify: (options) => requireIpcSupport().notify(options),
|
||||||
listConnectorModels: async (connectorId) => {
|
listConnectorModels: async (connectorId) => {
|
||||||
const definition = getConnectorDefinition(connectorId as Parameters<typeof getConnectorDefinition>[0]);
|
const definition = getConnectorDefinition(connectorId as Parameters<typeof getConnectorDefinition>[0]);
|
||||||
return definition.listModels ? definition.listModels() : [];
|
return definition.listModels ? definition.listModels() : [];
|
||||||
|
|||||||
Reference in new issue
Block a user