@penclipai/server 2026.531.0 → 2026.602.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attachment-types.d.ts.map +1 -1
- package/dist/attachment-types.js +8 -1
- package/dist/attachment-types.js.map +1 -1
- package/dist/onboarding-assets/default/AGENTS.md +1 -0
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +120 -9
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/plugins.d.ts.map +1 -1
- package/dist/routes/plugins.js +41 -2
- package/dist/routes/plugins.js.map +1 -1
- package/dist/services/npm-command.d.ts +10 -0
- package/dist/services/npm-command.d.ts.map +1 -1
- package/dist/services/npm-command.js +39 -0
- package/dist/services/npm-command.js.map +1 -1
- package/dist/services/plugin-loader.d.ts.map +1 -1
- package/dist/services/plugin-loader.js +114 -6
- package/dist/services/plugin-loader.js.map +1 -1
- package/dist/storage/local-disk-provider.d.ts.map +1 -1
- package/dist/storage/local-disk-provider.js +8 -2
- package/dist/storage/local-disk-provider.js.map +1 -1
- package/dist/storage/s3-provider.d.ts.map +1 -1
- package/dist/storage/s3-provider.js +1 -0
- package/dist/storage/s3-provider.js.map +1 -1
- package/dist/storage/service.js +2 -2
- package/dist/storage/service.js.map +1 -1
- package/dist/storage/types.d.ts +5 -1
- package/dist/storage/types.d.ts.map +1 -1
- package/package.json +17 -17
- package/skills/paperclip/SKILL.md +6 -0
- package/skills/paperclip/references/artifacts.md +44 -0
- package/skills/paperclip/scripts/paperclip-upload-artifact.sh +371 -0
- package/ui-dist/assets/{_basePickBy-ByMSfj39.js → _basePickBy-CeTFxbQW.js} +1 -1
- package/ui-dist/assets/{_baseUniq-DG1b_zmL.js → _baseUniq-D0ujdjSD.js} +1 -1
- package/ui-dist/assets/{arc-DcS9UkZl.js → arc-BO6fFqoX.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-VXUJARFQ-D5Kff5cm.js → architectureDiagram-VXUJARFQ-D2UpP8cQ.js} +1 -1
- package/ui-dist/assets/{blockDiagram-VD42YOAC-BH6dSBfQ.js → blockDiagram-VD42YOAC-BR95g_Bq.js} +1 -1
- package/ui-dist/assets/{browser-ponyfill-BfYTcaXM.js → browser-ponyfill-f87cfQ5P.js} +1 -1
- package/ui-dist/assets/{c4Diagram-YG6GDRKO-BAf-ehBE.js → c4Diagram-YG6GDRKO-DI5tPMqa.js} +1 -1
- package/ui-dist/assets/channel-CET4O6ry.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-C9391Aki.js → chunk-4BX2VUAB-DYVTUDCW.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-6rL-ZsOn.js → chunk-55IACEB6-D0QGIKOI.js} +1 -1
- package/ui-dist/assets/{chunk-B4BG7PRW-ImljqNzx.js → chunk-B4BG7PRW-DMGouDbX.js} +1 -1
- package/ui-dist/assets/{chunk-DI55MBZ5-BDdh18Rg.js → chunk-DI55MBZ5-CDoOezvn.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-7_L2tN7X.js → chunk-FMBD7UC4-BsnMSmde.js} +1 -1
- package/ui-dist/assets/{chunk-QN33PNHL-CzQKfq_z.js → chunk-QN33PNHL-DakMd-Np.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-XF2YykJj.js → chunk-QZHKN3VN-DqQUmo7x.js} +1 -1
- package/ui-dist/assets/{chunk-TZMSLE5B-BwX2ntiZ.js → chunk-TZMSLE5B-BhHr3bYf.js} +1 -1
- package/ui-dist/assets/classDiagram-2ON5EDUG-D0t_5_AY.js +1 -0
- package/ui-dist/assets/classDiagram-v2-WZHVMYZB-D0t_5_AY.js +1 -0
- package/ui-dist/assets/clone-CsJH_z-2.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-BbH4008b.js → cose-bilkent-S5V4N54A-Cu5Xt36m.js} +1 -1
- package/ui-dist/assets/{dagre-6UL2VRFP-b2pZICbR.js → dagre-6UL2VRFP-oB6IWi02.js} +1 -1
- package/ui-dist/assets/{diagram-PSM6KHXK-C4lbvPFi.js → diagram-PSM6KHXK-CvrdSiPO.js} +1 -1
- package/ui-dist/assets/{diagram-QEK2KX5R-BfpepdBy.js → diagram-QEK2KX5R-DQSpEaL6.js} +1 -1
- package/ui-dist/assets/{diagram-S2PKOQOG-DLVgiUi4.js → diagram-S2PKOQOG-Ckv-tSzv.js} +1 -1
- package/ui-dist/assets/{erDiagram-Q2GNP2WA-B2OluUW7.js → erDiagram-Q2GNP2WA-B_hA9waW.js} +1 -1
- package/ui-dist/assets/{flowDiagram-NV44I4VS-Cn2DdjAL.js → flowDiagram-NV44I4VS-Bb0ZY-IZ.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-JELNMOA3-CR227Gza.js → ganttDiagram-JELNMOA3-DKwCKDGB.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-V2S2FVAM-CeiCAnXp.js → gitGraphDiagram-V2S2FVAM-DAzn2J50.js} +1 -1
- package/ui-dist/assets/{graph-w9htXwiQ.js → graph-Cl-tjvPX.js} +1 -1
- package/ui-dist/assets/{index-CXLOfk6Z.js → index-8GC1rVe0.js} +1 -1
- package/ui-dist/assets/{index-mX6-5FRE.js → index-B4Zx0-UV.js} +1 -1
- package/ui-dist/assets/{index-Veai0rZp.js → index-BPtAEykT.js} +1 -1
- package/ui-dist/assets/{index-itLpbAYs.js → index-BsoFGxYP.js} +1 -1
- package/ui-dist/assets/{index-g35rE4vj.js → index-BtF-hm9b.js} +1 -1
- package/ui-dist/assets/{index-DE5MeDRO.js → index-C-t56Owg.js} +1 -1
- package/ui-dist/assets/{index-eh6Rv8FK.js → index-CQo-Yt-A.js} +1 -1
- package/ui-dist/assets/{index-CbL_lUhe.js → index-CbYaoEKW.js} +1 -1
- package/ui-dist/assets/{index-BBDrbFd2.js → index-CfPehaOb.js} +1 -1
- package/ui-dist/assets/{index-wbYobCWV.js → index-ChV-aw4S.js} +1 -1
- package/ui-dist/assets/{index-cx2uRcar.js → index-CwlKP6-n.js} +153 -153
- package/ui-dist/assets/{index-CghOG6Ex.js → index-DF7nqDl_.js} +1 -1
- package/ui-dist/assets/{index-Cuh9kSyD.js → index-DG38TZml.js} +1 -1
- package/ui-dist/assets/{index-BXd30uHo.js → index-DL1vj0uy.js} +1 -1
- package/ui-dist/assets/index-DMIAyVox.css +1 -0
- package/ui-dist/assets/{index-CdMaXyLo.js → index-DNDIa7NC.js} +1 -1
- package/ui-dist/assets/{index-9Pv0GfLY.js → index-Dl6o6n5E.js} +1 -1
- package/ui-dist/assets/{index-CDENxz3A.js → index-DlxftVuv.js} +1 -1
- package/ui-dist/assets/{index-DQlzKoOP.js → index-Do6HXiXB.js} +1 -1
- package/ui-dist/assets/{index-gEKPjjH6.js → index-DxJJ3F_W.js} +1 -1
- package/ui-dist/assets/{index-su6SxDiW.js → index-DzltSrZq.js} +1 -1
- package/ui-dist/assets/{index-DxCNY9q9.js → index-LLPT1gK0.js} +1 -1
- package/ui-dist/assets/{index-CXwrq9L0.js → index-km6491jM.js} +1 -1
- package/ui-dist/assets/{index-BYEl2-7l.js → index-u71Xs_3k.js} +1 -1
- package/ui-dist/assets/{infoDiagram-HS3SLOUP-eIIGpF4Y.js → infoDiagram-HS3SLOUP-DEL8kyRP.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-XKPGCS4Q-C-atAVVp.js → journeyDiagram-XKPGCS4Q-CRSQwCYB.js} +1 -1
- package/ui-dist/assets/{kanban-definition-3W4ZIXB7-DQqhMnWt.js → kanban-definition-3W4ZIXB7-B4y2cOh8.js} +1 -1
- package/ui-dist/assets/{layout-Bhpcjb2T.js → layout-BpvTQ45G.js} +1 -1
- package/ui-dist/assets/{linear-BlYGGdYI.js → linear-CYavthmK.js} +1 -1
- package/ui-dist/assets/{mermaid.core-BRPNlZ-h.js → mermaid.core-CYlgTsL-.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-VGOIOE7T-TDpHA9cf.js → mindmap-definition-VGOIOE7T-D5EKPjo-.js} +1 -1
- package/ui-dist/assets/{pieDiagram-ADFJNKIX-ETq0xhKM.js → pieDiagram-ADFJNKIX-C2JvyqX-.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-AYHSOK5B-Dtn2WJxU.js → quadrantDiagram-AYHSOK5B--5eHpwLO.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-UZGBJVZJ-DR_U4zGO.js → requirementDiagram-UZGBJVZJ-DWl0VcDt.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-TZEHDZUN-Tf0xWtIZ.js → sankeyDiagram-TZEHDZUN-DTVD0t60.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-WL72ISMW-DNiMMozs.js → sequenceDiagram-WL72ISMW-DqRlRIie.js} +1 -1
- package/ui-dist/assets/{stateDiagram-FKZM4ZOC-Dnd48Ana.js → stateDiagram-FKZM4ZOC-pmJVfUSI.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-tJvkm5fW.js +1 -0
- package/ui-dist/assets/{timeline-definition-IT6M3QCI-B5lOcm7v.js → timeline-definition-IT6M3QCI-D4a7yShw.js} +1 -1
- package/ui-dist/assets/{treemap-GDKQZRPO-DPalpZKu.js → treemap-GDKQZRPO-B99Tnc1-.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-PRI3JC2R-wR9L_1vP.js → xychartDiagram-PRI3JC2R-DtVbo4uj.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/locales/en/common.json +13 -0
- package/ui-dist/locales/zh-CN/common.json +13 -0
- package/ui-dist/assets/channel-DfI4Qn_9.js +0 -1
- package/ui-dist/assets/classDiagram-2ON5EDUG-CeFzDBRj.js +0 -1
- package/ui-dist/assets/classDiagram-v2-WZHVMYZB-CeFzDBRj.js +0 -1
- package/ui-dist/assets/clone-nd1JPeva.js +0 -1
- package/ui-dist/assets/index-DsG7g8TR.css +0 -1
- package/ui-dist/assets/stateDiagram-v2-4FDKWEC3-CxR0Vtv5.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-types.d.ts","sourceRoot":"","sources":["../src/attachment-types.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"attachment-types.d.ts","sourceRoot":"","sources":["../src/attachment-types.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAgBlD,CAAC;AAEF,eAAO,MAAM,+BAA+B,6BAA6B,CAAC;AAC1E,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAChD,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAUpD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAOnE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAS1F;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGnF;AAED,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE1E;AAQD,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,eAAO,MAAM,oBAAoB,QACuC,CAAC;AAEzE,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAKzF"}
|
package/dist/attachment-types.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared attachment content-type configuration.
|
|
3
3
|
*
|
|
4
|
-
* By default a curated set of image/document/text types are allowed. Set the
|
|
4
|
+
* By default a curated set of image/document/text/media types are allowed. Set the
|
|
5
5
|
* `PAPERCLIP_ALLOWED_ATTACHMENT_TYPES` environment variable to a
|
|
6
6
|
* comma-separated list of MIME types or wildcard patterns to expand the
|
|
7
7
|
* allowed set for routes that use this allowlist.
|
|
@@ -22,11 +22,15 @@ export const DEFAULT_ALLOWED_TYPES = [
|
|
|
22
22
|
"image/webp",
|
|
23
23
|
"image/gif",
|
|
24
24
|
"application/pdf",
|
|
25
|
+
"application/zip",
|
|
25
26
|
"text/markdown",
|
|
26
27
|
"text/plain",
|
|
27
28
|
"application/json",
|
|
28
29
|
"text/csv",
|
|
29
30
|
"text/html",
|
|
31
|
+
"video/mp4",
|
|
32
|
+
"video/webm",
|
|
33
|
+
"video/quicktime",
|
|
30
34
|
];
|
|
31
35
|
export const DEFAULT_ATTACHMENT_CONTENT_TYPE = "application/octet-stream";
|
|
32
36
|
export const SVG_CONTENT_TYPE = "image/svg+xml";
|
|
@@ -37,6 +41,9 @@ export const INLINE_ATTACHMENT_TYPES = [
|
|
|
37
41
|
"text/markdown",
|
|
38
42
|
"application/json",
|
|
39
43
|
"text/csv",
|
|
44
|
+
"video/mp4",
|
|
45
|
+
"video/webm",
|
|
46
|
+
"video/quicktime",
|
|
40
47
|
];
|
|
41
48
|
/**
|
|
42
49
|
* Parse a comma-separated list of MIME type patterns into a normalised array.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-types.js","sourceRoot":"","sources":["../src/attachment-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,oCAAoC,EACpC,gCAAgC,GACjC,MAAM,mBAAmB,CAAC;AAE3B,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,WAAW;IACX,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,WAAW;IACX,iBAAiB;IACjB,eAAe;IACf,YAAY;IACZ,kBAAkB;IAClB,UAAU;IACV,WAAW;
|
|
1
|
+
{"version":3,"file":"attachment-types.js","sourceRoot":"","sources":["../src/attachment-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ You are an agent at Paperclip company.
|
|
|
5
5
|
- Start actionable work in the same heartbeat. Do not stop at a plan unless the issue explicitly asks for planning.
|
|
6
6
|
- Keep the work moving until it is done. If you need QA to review it, ask them. If you need your boss to review it, ask them.
|
|
7
7
|
- Leave durable progress in task comments, documents, or work products, then update the issue to a clear final disposition before you exit.
|
|
8
|
+
- When your work produces a user-inspectable file, follow the Paperclip skill's "Generated Artifacts and Work Products" workflow before final disposition. Use `skills/paperclip/scripts/paperclip-upload-artifact.sh` when working in this repo, create/update an artifact work product when the file is the deliverable, and link the uploaded attachment in the final comment. Do not rely on local filesystem paths as the only access path.
|
|
8
9
|
- Comments, documents, screenshots, work products, and `Remaining` bullets are evidence, not valid liveness paths by themselves.
|
|
9
10
|
- Final disposition checklist: mark `done` when complete and verified; use `in_review` only with a real reviewer, approval, interaction, or monitor path; use `blocked` only with first-class blockers or a named unblock owner/action; create delegated follow-up issues with blockers when another agent owns the next step; keep `in_progress` only when a live continuation path exists.
|
|
10
11
|
- Use child issues for parallel or long delegated work instead of polling agents, sessions, or processes.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../../src/routes/issues.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAUxC,OAAO,
|
|
1
|
+
{"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../../src/routes/issues.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAUxC,OAAO,EAmCL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAI3B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAkD1D,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,0CAA0C,CAAC;AASlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AA8BhF,KAAK,oBAAoB,GAAG;IAC1B,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtF,CAAC;AAitBF,wBAAgB,WAAW,CACzB,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE;IACJ,qBAAqB,CAAC,EAAE;QACtB,0BAA0B,CAAC,KAAK,CAAC,EAAE;YACjC,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,GAAG,CAAC,EAAE,IAAI,CAAC;SACZ,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACtB,CAAC;IACF,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CACtC,8CAs3KP"}
|
package/dist/routes/issues.js
CHANGED
|
@@ -4,7 +4,7 @@ import multer from "multer";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { and, desc, eq, inArray, notInArray } from "drizzle-orm";
|
|
6
6
|
import { activityLog, executionWorkspaces, heartbeatRuns, issueExecutionDecisions, issueRelations, issues as issueRows, projectWorkspaces, } from "@penclipai/db";
|
|
7
|
-
import { addIssueCommentSchema, acceptIssueThreadInteractionSchema, cancelIssueThreadInteractionSchema, companySearchQuerySchema, createIssueAttachmentMetadataSchema, createIssueThreadInteractionSchema, createIssueWorkProductSchema, createIssueLabelSchema, createAcceptedPlanDecompositionSchema, checkoutIssueSchema, createDocumentAnnotationCommentSchema, createDocumentAnnotationThreadSchema, createChildIssueSchema, createIssueSchema, resolveCreateIssueStatusDefault, resolveIssueRecoveryActionSchema, feedbackTargetTypeSchema, feedbackTraceStatusSchema, feedbackVoteValueSchema, upsertIssueFeedbackVoteSchema, linkIssueApprovalSchema, issueDocumentKeySchema, ISSUE_CONTINUATION_SUMMARY_DOCUMENT_KEY, rejectIssueThreadInteractionSchema, restoreIssueDocumentRevisionSchema, respondIssueThreadInteractionSchema, updateIssueWorkProductSchema, updateDocumentAnnotationThreadSchema, upsertIssueDocumentSchema, updateIssueSchema, getClosedIsolatedExecutionWorkspaceMessage, isClosedIsolatedExecutionWorkspace, normalizeIssueIdentifier as normalizeIssueReferenceIdentifier, } from "@penclipai/shared";
|
|
7
|
+
import { addIssueCommentSchema, acceptIssueThreadInteractionSchema, attachmentArtifactWorkProductMetadataSchema, cancelIssueThreadInteractionSchema, companySearchQuerySchema, createIssueAttachmentMetadataSchema, createIssueThreadInteractionSchema, createIssueWorkProductSchema, createIssueLabelSchema, createAcceptedPlanDecompositionSchema, checkoutIssueSchema, createDocumentAnnotationCommentSchema, createDocumentAnnotationThreadSchema, createChildIssueSchema, createIssueSchema, resolveCreateIssueStatusDefault, resolveIssueRecoveryActionSchema, feedbackTargetTypeSchema, feedbackTraceStatusSchema, feedbackVoteValueSchema, upsertIssueFeedbackVoteSchema, linkIssueApprovalSchema, issueDocumentKeySchema, ISSUE_CONTINUATION_SUMMARY_DOCUMENT_KEY, rejectIssueThreadInteractionSchema, restoreIssueDocumentRevisionSchema, respondIssueThreadInteractionSchema, updateIssueWorkProductSchema, updateDocumentAnnotationThreadSchema, upsertIssueDocumentSchema, updateIssueSchema, getClosedIsolatedExecutionWorkspaceMessage, isClosedIsolatedExecutionWorkspace, normalizeIssueIdentifier as normalizeIssueReferenceIdentifier, } from "@penclipai/shared";
|
|
8
8
|
import { getTelemetryClient } from "../telemetry.js";
|
|
9
9
|
import { trackAgentTaskCompleted } from "@penclipai/shared/telemetry";
|
|
10
10
|
import { validate } from "../middleware/validate.js";
|
|
@@ -15,7 +15,7 @@ import { conflict, forbidden, HttpError, notFound, unprocessable } from "../erro
|
|
|
15
15
|
import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
|
|
16
16
|
import { assertNoAgentHostWorkspaceCommandMutation, collectIssueWorkspaceCommandPaths, } from "./workspace-command-authz.js";
|
|
17
17
|
import { shouldWakeAssigneeOnCheckout } from "./issues-checkout-wakeup.js";
|
|
18
|
-
import { isInlineAttachmentContentType, normalizeIssueAttachmentMaxBytes, normalizeContentType, SVG_CONTENT_TYPE, } from "../attachment-types.js";
|
|
18
|
+
import { isInlineAttachmentContentType, isAllowedContentType, normalizeIssueAttachmentMaxBytes, normalizeContentType, SVG_CONTENT_TYPE, } from "../attachment-types.js";
|
|
19
19
|
import { queueIssueAssignmentWakeup } from "../services/issue-assignment-wakeup.js";
|
|
20
20
|
import { resolveExplicitRequestUiLocale } from "../ui-locale.js";
|
|
21
21
|
import { assertEnvironmentSelectionForCompany } from "./environment-selection.js";
|
|
@@ -60,6 +60,17 @@ function applyCreateIssueStatusDefault(req, res, next) {
|
|
|
60
60
|
}
|
|
61
61
|
next();
|
|
62
62
|
}
|
|
63
|
+
function buildAttachmentContentPath(attachmentId) {
|
|
64
|
+
return `/api/attachments/${attachmentId}/content`;
|
|
65
|
+
}
|
|
66
|
+
function requiresPaperclipAttachmentMetadata(input, fallback) {
|
|
67
|
+
const type = typeof input.type === "string" ? input.type : fallback?.type ?? null;
|
|
68
|
+
const provider = typeof input.provider === "string" ? input.provider : fallback?.provider ?? null;
|
|
69
|
+
return type === "artifact" && provider === "paperclip";
|
|
70
|
+
}
|
|
71
|
+
const attachmentArtifactMetadataInputSchema = z.object({
|
|
72
|
+
attachmentId: z.string().uuid(),
|
|
73
|
+
}).passthrough();
|
|
63
74
|
function buildCreateIssueActivityStatusDetails(issue, res) {
|
|
64
75
|
const statusDefault = res.locals.createIssueStatusDefault;
|
|
65
76
|
const assignmentWakeSkipped = !issue.assigneeAgentId || issue.status === "backlog";
|
|
@@ -737,11 +748,43 @@ export function issueRoutes(db, storage, opts = {}) {
|
|
|
737
748
|
}
|
|
738
749
|
}
|
|
739
750
|
function withContentPath(attachment) {
|
|
751
|
+
const contentPath = `/api/attachments/${attachment.id}/content`;
|
|
740
752
|
return {
|
|
741
753
|
...attachment,
|
|
742
|
-
contentPath
|
|
754
|
+
contentPath,
|
|
755
|
+
openPath: contentPath,
|
|
756
|
+
downloadPath: `${contentPath}?download=1`,
|
|
743
757
|
};
|
|
744
758
|
}
|
|
759
|
+
function parseAttachmentRangeHeader(raw, contentLength) {
|
|
760
|
+
if (!raw)
|
|
761
|
+
return { kind: "none" };
|
|
762
|
+
if (!Number.isSafeInteger(contentLength) || contentLength <= 0)
|
|
763
|
+
return { kind: "invalid" };
|
|
764
|
+
const prefix = "bytes=";
|
|
765
|
+
if (!raw.toLowerCase().startsWith(prefix))
|
|
766
|
+
return { kind: "invalid" };
|
|
767
|
+
const spec = raw.slice(prefix.length).trim();
|
|
768
|
+
if (!spec || spec.includes(","))
|
|
769
|
+
return { kind: "invalid" };
|
|
770
|
+
const [startRaw, endRaw] = spec.split("-", 2);
|
|
771
|
+
if (endRaw === undefined)
|
|
772
|
+
return { kind: "invalid" };
|
|
773
|
+
if (startRaw === "") {
|
|
774
|
+
const suffixLength = Number.parseInt(endRaw, 10);
|
|
775
|
+
if (!Number.isSafeInteger(suffixLength) || suffixLength <= 0)
|
|
776
|
+
return { kind: "invalid" };
|
|
777
|
+
const start = Math.max(contentLength - suffixLength, 0);
|
|
778
|
+
return { kind: "range", start, end: contentLength - 1 };
|
|
779
|
+
}
|
|
780
|
+
const start = Number.parseInt(startRaw, 10);
|
|
781
|
+
if (!Number.isSafeInteger(start) || start < 0 || start >= contentLength)
|
|
782
|
+
return { kind: "invalid" };
|
|
783
|
+
const end = endRaw === "" ? contentLength - 1 : Number.parseInt(endRaw, 10);
|
|
784
|
+
if (!Number.isSafeInteger(end) || end < start)
|
|
785
|
+
return { kind: "invalid" };
|
|
786
|
+
return { kind: "range", start, end: Math.min(end, contentLength - 1) };
|
|
787
|
+
}
|
|
745
788
|
function parseBooleanQuery(value) {
|
|
746
789
|
return value === true || value === "true" || value === "1";
|
|
747
790
|
}
|
|
@@ -800,6 +843,32 @@ export function issueRoutes(db, storage, opts = {}) {
|
|
|
800
843
|
annotationCommentId: input.commentId,
|
|
801
844
|
}, "failed to wake assignee on document annotation comment"));
|
|
802
845
|
}
|
|
846
|
+
async function canonicalizePaperclipArtifactMetadata(input) {
|
|
847
|
+
const parsed = attachmentArtifactMetadataInputSchema.safeParse(input.metadata);
|
|
848
|
+
if (!parsed.success) {
|
|
849
|
+
throw unprocessable("Invalid attachment artifact metadata", {
|
|
850
|
+
code: "invalid_attachment_artifact_metadata",
|
|
851
|
+
details: parsed.error.issues,
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
const attachment = await svc.getAttachmentById(parsed.data.attachmentId);
|
|
855
|
+
if (!attachment || attachment.companyId !== input.issue.companyId || attachment.issueId !== input.issue.id) {
|
|
856
|
+
throw unprocessable("Attachment artifact must reference an attachment on the same issue", {
|
|
857
|
+
code: "invalid_attachment_artifact_metadata",
|
|
858
|
+
attachmentId: parsed.data.attachmentId,
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
const contentPath = buildAttachmentContentPath(attachment.id);
|
|
862
|
+
return attachmentArtifactWorkProductMetadataSchema.parse({
|
|
863
|
+
attachmentId: attachment.id,
|
|
864
|
+
contentType: normalizeContentType(attachment.contentType),
|
|
865
|
+
byteSize: attachment.byteSize,
|
|
866
|
+
contentPath,
|
|
867
|
+
openPath: contentPath,
|
|
868
|
+
downloadPath: `${contentPath}?download=1`,
|
|
869
|
+
originalFilename: attachment.originalFilename ?? null,
|
|
870
|
+
});
|
|
871
|
+
}
|
|
803
872
|
async function assertIssueEnvironmentSelection(companyId, environmentId) {
|
|
804
873
|
if (environmentId === undefined || environmentId === null)
|
|
805
874
|
return;
|
|
@@ -2531,10 +2600,17 @@ export function issueRoutes(db, storage, opts = {}) {
|
|
|
2531
2600
|
return;
|
|
2532
2601
|
if (!(await assertDeliverableMutationAllowedByRunContext(req, res, issue)))
|
|
2533
2602
|
return;
|
|
2534
|
-
const
|
|
2603
|
+
const createInput = {
|
|
2535
2604
|
...req.body,
|
|
2536
2605
|
projectId: req.body.projectId ?? issue.projectId ?? null,
|
|
2537
|
-
}
|
|
2606
|
+
};
|
|
2607
|
+
if (requiresPaperclipAttachmentMetadata(createInput)) {
|
|
2608
|
+
createInput.metadata = await canonicalizePaperclipArtifactMetadata({
|
|
2609
|
+
issue,
|
|
2610
|
+
metadata: req.body.metadata ?? null,
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
const product = await workProductsSvc.createForIssue(issue.id, issue.companyId, createInput);
|
|
2538
2614
|
if (!product) {
|
|
2539
2615
|
res.status(422).json({ error: "Invalid work product payload" });
|
|
2540
2616
|
return;
|
|
@@ -2576,7 +2652,20 @@ export function issueRoutes(db, storage, opts = {}) {
|
|
|
2576
2652
|
return;
|
|
2577
2653
|
if (!(await assertDeliverableMutationAllowedByRunContext(req, res, issue)))
|
|
2578
2654
|
return;
|
|
2579
|
-
const
|
|
2655
|
+
const patch = { ...req.body };
|
|
2656
|
+
if (requiresPaperclipAttachmentMetadata(patch, existing)) {
|
|
2657
|
+
if (patch.metadata !== undefined) {
|
|
2658
|
+
patch.metadata = await canonicalizePaperclipArtifactMetadata({
|
|
2659
|
+
issue,
|
|
2660
|
+
metadata: patch.metadata ?? null,
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2663
|
+
else if (!requiresPaperclipAttachmentMetadata(existing)) {
|
|
2664
|
+
res.status(422).json({ error: "Attachment-backed artifact metadata is required" });
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
const product = await workProductsSvc.update(id, patch);
|
|
2580
2669
|
if (!product) {
|
|
2581
2670
|
res.status(404).json({ error: "Work product not found" });
|
|
2582
2671
|
return;
|
|
@@ -5161,6 +5250,10 @@ export function issueRoutes(db, storage, opts = {}) {
|
|
|
5161
5250
|
res.status(422).json({ error: "Attachment is empty" });
|
|
5162
5251
|
return;
|
|
5163
5252
|
}
|
|
5253
|
+
if (!isAllowedContentType(contentType)) {
|
|
5254
|
+
res.status(422).json({ error: `Unsupported attachment content type: ${contentType}` });
|
|
5255
|
+
return;
|
|
5256
|
+
}
|
|
5164
5257
|
const parsedMeta = createIssueAttachmentMetadataSchema.safeParse(req.body ?? {});
|
|
5165
5258
|
if (!parsedMeta.success) {
|
|
5166
5259
|
res.status(400).json({ error: "Invalid attachment metadata", details: parsedMeta.error.issues });
|
|
@@ -5212,21 +5305,39 @@ export function issueRoutes(db, storage, opts = {}) {
|
|
|
5212
5305
|
return;
|
|
5213
5306
|
}
|
|
5214
5307
|
assertCompanyAccess(req, attachment.companyId);
|
|
5215
|
-
const
|
|
5308
|
+
const contentLength = attachment.byteSize;
|
|
5309
|
+
const range = parseAttachmentRangeHeader(typeof req.headers.range === "string" ? req.headers.range : undefined, contentLength);
|
|
5310
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
5311
|
+
if (range.kind === "invalid") {
|
|
5312
|
+
res.setHeader("Content-Range", `bytes */${contentLength}`);
|
|
5313
|
+
res.status(416).end();
|
|
5314
|
+
return;
|
|
5315
|
+
}
|
|
5316
|
+
const object = await storage.getObject(attachment.companyId, attachment.objectKey, range.kind === "range" ? { range: { start: range.start, end: range.end } } : undefined);
|
|
5216
5317
|
const responseContentType = normalizeContentType(attachment.contentType || object.contentType);
|
|
5217
5318
|
res.setHeader("Content-Type", responseContentType);
|
|
5218
|
-
res.setHeader("Content-Length", String(attachment.byteSize || object.contentLength || 0));
|
|
5219
5319
|
res.setHeader("Cache-Control", "private, max-age=60");
|
|
5220
5320
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
5221
5321
|
if (responseContentType === SVG_CONTENT_TYPE) {
|
|
5222
5322
|
res.setHeader("Content-Security-Policy", "sandbox; default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'");
|
|
5223
5323
|
}
|
|
5224
5324
|
const filename = attachment.originalFilename ?? "attachment";
|
|
5225
|
-
const disposition =
|
|
5325
|
+
const disposition = parseBooleanQuery(req.query.download)
|
|
5326
|
+
? "attachment"
|
|
5327
|
+
: isInlineAttachmentContentType(responseContentType) ? "inline" : "attachment";
|
|
5226
5328
|
res.setHeader("Content-Disposition", `${disposition}; filename=\"${filename.replaceAll("\"", "")}\"`);
|
|
5227
5329
|
object.stream.on("error", (err) => {
|
|
5228
5330
|
next(err);
|
|
5229
5331
|
});
|
|
5332
|
+
if (range.kind === "range") {
|
|
5333
|
+
const rangeLength = range.end - range.start + 1;
|
|
5334
|
+
res.status(206);
|
|
5335
|
+
res.setHeader("Content-Length", String(rangeLength));
|
|
5336
|
+
res.setHeader("Content-Range", `bytes ${range.start}-${range.end}/${contentLength}`);
|
|
5337
|
+
object.stream.pipe(res);
|
|
5338
|
+
return;
|
|
5339
|
+
}
|
|
5340
|
+
res.setHeader("Content-Length", String(contentLength || object.contentLength || 0));
|
|
5230
5341
|
object.stream.pipe(res);
|
|
5231
5342
|
});
|
|
5232
5343
|
router.delete("/attachments/:attachmentId", async (req, res) => {
|