Route work-product clicks through a resolver IPC

Filesystem work products store `external_id` as a workspace-relative
path so artifacts survive a workspace-root move, but the renderer was
handing that relative fragment straight to `shell.openPath`, which
needs an absolute managed path. Clicks flickered into a failed open.

Add a dedicated `openWorkProduct({ companyId, id })` IPC backed by
`resolveWorkProductTarget` on the DB layer. The resolver joins the
work-product row back to its originating task → workspace (or run →
workspace for taskless runs), composes an absolute path, and rejects
any relative fragment that would escape the workspace root. The
renderer now passes the work product's id — path resolution stays on
the main process where the managed-path policy already lives.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-19 15:05:21 -04:00
1 parent 1a0fdc6f31
commit ff9a52324e
7 files changed
+113 -8

No files matched your search

+1
View File
@@ -2,6 +2,7 @@ export const IPC_CHANNELS = {
loadSnapshot: "agent-company:load-snapshot",
pickDirectory: "agent-company:pick-directory",
openPath: "agent-company:open-path",
openWorkProduct: "agent-company:open-work-product",
saveCompany: "agent-company:save-company",
setCurrentCompany: "agent-company:set-current-company",
saveConnector: "agent-company:save-connector",
@@ -1,13 +1,14 @@
import type { z } from "zod";
import type { CompanyMetrics, OnboardingBootstrapResult, ProfileSnapshot, StandupReport } from "../../types";
import type { bootstrapOnboardingSchema, companyInputSchema, connectorDisabledModelsSchema, connectorInputSchema, settingsSchema } from "../schemas-company";
import type { metricsQuerySchema } from "../schemas-badges";
import type { entityGetterSchema, metricsQuerySchema } from "../schemas-badges";
import type { DesktopResult } from "./common";
export type CompaniesApi = {
loadSnapshot: () => Promise<DesktopResult<ProfileSnapshot>>;
pickDirectory: () => Promise<DesktopResult<string | null>>;
openPath: (targetPath: string) => Promise<DesktopResult<boolean>>;
openWorkProduct: (input: z.infer<typeof entityGetterSchema>) => Promise<DesktopResult<boolean>>;
saveCompany: (input: z.infer<typeof companyInputSchema>) => Promise<DesktopResult<string>>;
setCurrentCompany: (companyId: string) => Promise<DesktopResult<boolean>>;
saveConnector: (input: z.infer<typeof connectorInputSchema>) => Promise<DesktopResult<boolean>>;