Skip to content

Commit c2c2813

Browse files
committed
Refactor agent service
1 parent b2f32c9 commit c2c2813

File tree

21 files changed

+474
-559
lines changed

21 files changed

+474
-559
lines changed

apps/array/src/main/di/container.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "reflect-metadata";
22
import { Container } from "inversify";
3+
import { AgentService } from "../services/agent/service.js";
34
import { ContextMenuService } from "../services/context-menu/service.js";
45
import { DeepLinkService } from "../services/deep-link/service.js";
56
import { DockBadgeService } from "../services/dock-badge/service.js";
@@ -11,6 +12,7 @@ import { GitService } from "../services/git/service.js";
1112
import { OAuthService } from "../services/oauth/service.js";
1213
import { ShellService } from "../services/shell/service.js";
1314
import { TaskLinkService } from "../services/task-link/service.js";
15+
import { UIService } from "../services/ui/service.js";
1416
import { UpdatesService } from "../services/updates/service.js";
1517
import { WorkspaceService } from "../services/workspace/service.js";
1618
import { MAIN_TOKENS } from "./tokens.js";
@@ -19,6 +21,7 @@ export const container = new Container({
1921
defaultScope: "Singleton",
2022
});
2123

24+
container.bind(MAIN_TOKENS.AgentService).to(AgentService);
2225
container.bind(MAIN_TOKENS.ContextMenuService).to(ContextMenuService);
2326
container.bind(MAIN_TOKENS.DeepLinkService).to(DeepLinkService);
2427
container.bind(MAIN_TOKENS.DockBadgeService).to(DockBadgeService);
@@ -29,6 +32,7 @@ container.bind(MAIN_TOKENS.FsService).to(FsService);
2932
container.bind(MAIN_TOKENS.GitService).to(GitService);
3033
container.bind(MAIN_TOKENS.OAuthService).to(OAuthService);
3134
container.bind(MAIN_TOKENS.ShellService).to(ShellService);
35+
container.bind(MAIN_TOKENS.UIService).to(UIService);
3236
container.bind(MAIN_TOKENS.UpdatesService).to(UpdatesService);
3337
container.bind(MAIN_TOKENS.TaskLinkService).to(TaskLinkService);
3438
container.bind(MAIN_TOKENS.WorkspaceService).to(WorkspaceService);

apps/array/src/main/di/tokens.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
export const MAIN_TOKENS = Object.freeze({
88
// Services
9+
AgentService: Symbol.for("Main.AgentService"),
910
ContextMenuService: Symbol.for("Main.ContextMenuService"),
1011
DockBadgeService: Symbol.for("Main.DockBadgeService"),
1112
ExternalAppsService: Symbol.for("Main.ExternalAppsService"),
@@ -17,6 +18,7 @@ export const MAIN_TOKENS = Object.freeze({
1718
OAuthService: Symbol.for("Main.OAuthService"),
1819
TaskLinkService: Symbol.for("Main.TaskLinkService"),
1920
ShellService: Symbol.for("Main.ShellService"),
21+
UIService: Symbol.for("Main.UIService"),
2022
UpdatesService: Symbol.for("Main.UpdatesService"),
2123
WorkspaceService: Symbol.for("Main.WorkspaceService"),
2224
});

apps/array/src/main/index.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
BrowserWindow,
1313
clipboard,
1414
dialog,
15-
ipcMain,
1615
Menu,
1716
type MenuItemConstructorOptions,
1817
shell,
@@ -22,17 +21,12 @@ import "./lib/logger";
2221
import { ANALYTICS_EVENTS } from "../types/analytics.js";
2322
import { container } from "./di/container.js";
2423
import { MAIN_TOKENS } from "./di/tokens.js";
24+
import type { AgentService } from "./services/agent/service.js";
2525
import type { DockBadgeService } from "./services/dock-badge/service.js";
26-
import {
27-
cleanupAgentSessions,
28-
registerAgentIpc,
29-
} from "./services/session-manager.js";
26+
import type { UIService } from "./services/ui/service.js";
3027
import { setMainWindowGetter } from "./trpc/context.js";
3128
import { trpcRouter } from "./trpc/index.js";
3229

33-
// Legacy type kept for backwards compatibility with taskControllers map
34-
type TaskController = unknown;
35-
3630
import "./services/index.js";
3731
import type { DeepLinkService } from "./services/deep-link/service.js";
3832
import type { ExternalAppsService } from "./services/external-apps/service.js";
@@ -52,7 +46,6 @@ declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string | undefined;
5246
declare const MAIN_WINDOW_VITE_NAME: string;
5347

5448
let mainWindow: BrowserWindow | null = null;
55-
const taskControllers = new Map<string, TaskController>();
5649

5750
// Force IPv4 resolution when "localhost" is used so the agent hits 127.0.0.1
5851
// instead of ::1. This matches how the renderer already reaches the PostHog API.
@@ -223,7 +216,7 @@ function createWindow(): void {
223216
label: "Settings...",
224217
accelerator: "CmdOrCtrl+,",
225218
click: () => {
226-
mainWindow?.webContents.send("open-settings");
219+
container.get<UIService>(MAIN_TOKENS.UIService).openSettings();
227220
},
228221
},
229222
{ type: "separator" },
@@ -237,7 +230,7 @@ function createWindow(): void {
237230
label: "New task",
238231
accelerator: "CmdOrCtrl+N",
239232
click: () => {
240-
mainWindow?.webContents.send("new-task");
233+
container.get<UIService>(MAIN_TOKENS.UIService).newTask();
241234
},
242235
},
243236
{ type: "separator" },
@@ -247,7 +240,7 @@ function createWindow(): void {
247240
{
248241
label: "Clear application storage",
249242
click: () => {
250-
mainWindow?.webContents.send("clear-storage");
243+
container.get<UIService>(MAIN_TOKENS.UIService).clearStorage();
251244
},
252245
},
253246
],
@@ -282,7 +275,7 @@ function createWindow(): void {
282275
{
283276
label: "Reset layout",
284277
click: () => {
285-
mainWindow?.webContents.send("reset-layout");
278+
container.get<UIService>(MAIN_TOKENS.UIService).resetLayout();
286279
},
287280
},
288281
],
@@ -356,7 +349,8 @@ app.on("window-all-closed", async () => {
356349

357350
app.on("before-quit", async (event) => {
358351
event.preventDefault();
359-
await cleanupAgentSessions();
352+
const agentService = container.get<AgentService>(MAIN_TOKENS.AgentService);
353+
await agentService.cleanupAll();
360354
trackAppEvent(ANALYTICS_EVENTS.APP_QUIT);
361355
await shutdownPostHog();
362356
app.exit(0);
@@ -367,8 +361,3 @@ app.on("activate", () => {
367361
createWindow();
368362
}
369363
});
370-
371-
ipcMain.handle("app:get-version", () => app.getVersion());
372-
373-
// Register IPC handlers via services
374-
registerAgentIpc(taskControllers, () => mainWindow);

apps/array/src/main/ipc/createIpcService.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

apps/array/src/main/lib/ipcHandler.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

apps/array/src/main/preload.ts

Lines changed: 1 addition & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,6 @@
1-
import type { ContentBlock } from "@agentclientprotocol/sdk";
2-
import { contextBridge, type IpcRendererEvent, ipcRenderer } from "electron";
31
import { exposeElectronTRPC } from "trpc-electron/main";
42
import "electron-log/preload";
53

6-
process.once("loaded", () => {
4+
process.once("loaded", async () => {
75
exposeElectronTRPC();
86
});
9-
10-
/// -- Legacy IPC handlers -- ///
11-
12-
type IpcEventListener<T> = (data: T) => void;
13-
14-
function createIpcListener<T>(
15-
channel: string,
16-
listener: IpcEventListener<T>,
17-
): () => void {
18-
const wrapped = (_event: IpcRendererEvent, data: T) => listener(data);
19-
ipcRenderer.on(channel, wrapped);
20-
return () => ipcRenderer.removeListener(channel, wrapped);
21-
}
22-
23-
function createVoidIpcListener(
24-
channel: string,
25-
listener: () => void,
26-
): () => void {
27-
ipcRenderer.on(channel, listener);
28-
return () => ipcRenderer.removeListener(channel, listener);
29-
}
30-
31-
interface AgentStartParams {
32-
taskId: string;
33-
taskRunId: string;
34-
repoPath: string;
35-
apiKey: string;
36-
apiHost: string;
37-
projectId: number;
38-
permissionMode?: string;
39-
autoProgress?: boolean;
40-
model?: string;
41-
executionMode?: "plan";
42-
runMode?: "local" | "cloud";
43-
createPR?: boolean;
44-
}
45-
46-
contextBridge.exposeInMainWorld("electronAPI", {
47-
// Agent API
48-
agentStart: async (
49-
params: AgentStartParams,
50-
): Promise<{ sessionId: string; channel: string }> =>
51-
ipcRenderer.invoke("agent-start", params),
52-
agentPrompt: async (
53-
sessionId: string,
54-
prompt: ContentBlock[],
55-
): Promise<{ stopReason: string }> =>
56-
ipcRenderer.invoke("agent-prompt", sessionId, prompt),
57-
agentCancel: async (sessionId: string): Promise<boolean> =>
58-
ipcRenderer.invoke("agent-cancel", sessionId),
59-
agentCancelPrompt: async (sessionId: string): Promise<boolean> =>
60-
ipcRenderer.invoke("agent-cancel-prompt", sessionId),
61-
agentReconnect: async (params: {
62-
taskId: string;
63-
taskRunId: string;
64-
repoPath: string;
65-
apiKey: string;
66-
apiHost: string;
67-
projectId: number;
68-
logUrl?: string;
69-
sdkSessionId?: string;
70-
}): Promise<{ sessionId: string; channel: string } | null> =>
71-
ipcRenderer.invoke("agent-reconnect", params),
72-
agentTokenRefresh: async (
73-
taskRunId: string,
74-
newToken: string,
75-
): Promise<void> =>
76-
ipcRenderer.invoke("agent-token-refresh", taskRunId, newToken),
77-
agentSetModel: async (sessionId: string, modelId: string): Promise<void> =>
78-
ipcRenderer.invoke("agent-set-model", sessionId, modelId),
79-
onAgentEvent: (
80-
channel: string,
81-
listener: (payload: unknown) => void,
82-
): (() => void) => createIpcListener(channel, listener),
83-
onOpenSettings: (listener: () => void): (() => void) =>
84-
createVoidIpcListener("open-settings", listener),
85-
onNewTask: (listener: () => void): (() => void) =>
86-
createVoidIpcListener("new-task", listener),
87-
onResetLayout: (listener: () => void): (() => void) =>
88-
createVoidIpcListener("reset-layout", listener),
89-
onClearStorage: (listener: () => void): (() => void) =>
90-
createVoidIpcListener("clear-storage", listener),
91-
getAppVersion: (): Promise<string> => ipcRenderer.invoke("app:get-version"),
92-
});

0 commit comments

Comments
 (0)