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

@@ -65,18 +65,21 @@ function DocumentEditor({ companyId, agents, projects, goals, document, onSaved
);
}
function WorkProductRow({ product }: { product: WorkProductRecord }) {
function WorkProductRow({ product, companyId }: { product: WorkProductRecord; companyId: string }) {
const subtitle = [
product.type.replaceAll("_", " "),
product.provider,
product.externalId ?? product.url ?? null,
product.updatedAt.slice(0, 10),
].filter((v): v is string => Boolean(v)).join(" · ");
const primaryAction = product.url
? () => { void window.agentCompany.openPath(product.url!); }
: product.provider === "filesystem" && product.externalId
? () => { void window.agentCompany.openPath(product.externalId!); }
: null;
// externalId is workspace-relative; route through openWorkProduct so
// the main process can join it against the work product's workspace
// root before handing off to the OS shell. Direct `openPath(externalId)`
// used to flash a failure when the renderer tried to treat the relative
// path as absolute.
const primaryAction = product.url || (product.provider === "filesystem" && product.externalId)
? () => { void window.agentCompany.openWorkProduct({ companyId, id: product.id }); }
: null;
const rowBody = (
<div className="min-w-0 flex-1 text-left">
<div className="flex items-center gap-2 text-[13px] font-medium text-foreground">
@@ -155,7 +158,7 @@ export function DocumentCenter({ companyId, documents, workProducts, agents, pro
</div>
<ListFrame>
{workProducts.map((product) => (
<WorkProductRow key={product.id} product={product} />
<WorkProductRow key={product.id} product={product} companyId={companyId} />
))}
</ListFrame>
</div>