fix(runner): reject unpublished workspace file receipts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
417a96011a
commit
0be8ba9d97
|
|
@ -1207,7 +1207,9 @@ that the task is complete.
|
|||
For an explicit file output in the current request, an empty report, a
|
||||
verification-only reference, or an unregistered URL cannot satisfy delivery.
|
||||
The report must cite a task-scoped attachment or a registered accessible work
|
||||
product. Reading or reviewing an existing file for an inline answer does not
|
||||
product with a published URL. A `workspace_file` locator alone is not delivery
|
||||
evidence: it neither verifies the file nor preserves its bytes after cleanup.
|
||||
Reading or reviewing an existing file for an inline answer does not
|
||||
require uploading that input. Ambiguous prose remains subject to the runner's
|
||||
completion contract; the server's explicit-output check is deliberately narrow.
|
||||
|
||||
|
|
|
|||
|
|
@ -84,9 +84,10 @@ export async function validateNativeDeliverableEvidence(
|
|||
)) : [];
|
||||
const accessibleProduct = products.some(product => {
|
||||
if (["failed", "cancelled", "archived"].includes(product.status)) return false;
|
||||
const resource = product.metadata?.resourceRef as { kind?: unknown; path?: unknown } | undefined;
|
||||
const accessible = (typeof product.url === "string" && /^https?:\/\//iu.test(product.url)) ||
|
||||
(resource?.kind === "workspace_file" && typeof resource.path === "string" && resource.path.length > 0);
|
||||
// A workspace_file resource is only a locator: registration neither checks
|
||||
// its current bytes nor keeps them alive after workspace cleanup. Requested
|
||||
// files need a published URL or the verified attachment receipt above.
|
||||
const accessible = typeof product.url === "string" && /^https?:\/\//iu.test(product.url);
|
||||
return accessible && [product.url, `work_product:${product.id}`, `work-product:${product.id}`, `artifact:${product.id}`]
|
||||
.some(ref => typeof ref === "string" && refs.has(ref));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -226,9 +226,11 @@ describe("native runner file handoff", () => {
|
|||
await expect(nativeCompletionFeedback(db, runId, doneReport([ref])))
|
||||
.resolves.toContain("Completion report accepted");
|
||||
}
|
||||
await db.update(issueWorkProducts).set({ url: null, metadata: { resourceRef: { kind: "workspace_file", path: "report.pdf" } } }).where(eq(issueWorkProducts.id, product.id));
|
||||
// Metadata can name a nonexistent or cleaned-up workspace file; it is not a download.
|
||||
await db.update(issueWorkProducts).set({ url: null, metadata: { resourceRef: { kind: "workspace_file", path: "missing-report.pdf" } } }).where(eq(issueWorkProducts.id, product.id));
|
||||
await expect(nativeCompletionFeedback(db, runId, doneReport([`work_product:${product.id}`])))
|
||||
.resolves.toContain("Completion report accepted");
|
||||
.rejects.toThrow("requested file has no accessible delivery evidence");
|
||||
await db.update(issueWorkProducts).set({ url: product.url }).where(eq(issueWorkProducts.id, product.id));
|
||||
await db.update(issueWorkProducts).set({ status: "failed" }).where(eq(issueWorkProducts.id, product.id));
|
||||
await expect(nativeCompletionFeedback(db, runId, doneReport([`work_product:${product.id}`])))
|
||||
.rejects.toThrow("requested file has no accessible delivery evidence");
|
||||
|
|
|
|||
Loading…
Reference in New Issue