Add a new ai:action IPC channel so the renderer can react to AI tool side-effects (task creation, checkpoint/task suggestions). Also mark AI-created tasks with isAiSuggested: 1 in both project and global add_task tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
83 lines
2.2 KiB
TypeScript
83 lines
2.2 KiB
TypeScript
/**
|
|
* Renderer-side tRPC IPC link for Electron.
|
|
*
|
|
* Replaces electron-trpc's ipcLink with a custom implementation that
|
|
* works with our custom IPC handler + tRPC v11.
|
|
*/
|
|
import { observable } from '@trpc/server/observable';
|
|
import type { TRPCLink } from '@trpc/client';
|
|
import type { AnyRouter } from '@trpc/server';
|
|
|
|
interface ElectronTRPC {
|
|
sendMessage: (msg: unknown) => void;
|
|
onMessage: (cb: (data: unknown) => void) => (() => void) | void;
|
|
}
|
|
|
|
interface ElectronAI {
|
|
onStreamChunk: (cb: (data: { token: string; done: boolean }) => void) => () => void;
|
|
onAction: (cb: (data: { type: string; taskId?: string; count?: number }) => void) => () => void;
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
electronTRPC: ElectronTRPC;
|
|
electronAI: ElectronAI;
|
|
}
|
|
}
|
|
|
|
type TRPCResponse = {
|
|
id: number | null;
|
|
result?: { type: string; data?: unknown };
|
|
error?: unknown;
|
|
};
|
|
|
|
let nextId = 0;
|
|
|
|
export function ipcLink<TRouter extends AnyRouter>(): TRPCLink<TRouter> {
|
|
return () =>
|
|
({ op }) =>
|
|
observable((observer) => {
|
|
const id = ++nextId;
|
|
const { electronTRPC } = window;
|
|
|
|
if (!electronTRPC) {
|
|
observer.error(
|
|
new Error(
|
|
'Could not find `electronTRPC` global. ' +
|
|
'Check that the preload script has been loaded.',
|
|
) as any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
);
|
|
return;
|
|
}
|
|
|
|
const unsubscribe = electronTRPC.onMessage((response: unknown) => {
|
|
const msg = response as TRPCResponse;
|
|
if (msg.id !== id) return;
|
|
|
|
if ('error' in msg) {
|
|
observer.error(msg.error as any); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
return;
|
|
}
|
|
|
|
observer.next({
|
|
result: msg.result as { type: 'data'; data: unknown },
|
|
});
|
|
observer.complete();
|
|
});
|
|
|
|
electronTRPC.sendMessage({
|
|
method: 'request',
|
|
operation: {
|
|
id,
|
|
type: op.type,
|
|
path: op.path,
|
|
input: op.input,
|
|
},
|
|
});
|
|
|
|
return () => {
|
|
if (typeof unsubscribe === 'function') unsubscribe();
|
|
};
|
|
});
|
|
}
|