feat: 完善会商产物提交匹配与流式写文件预览
席位流程图按已提交 Markdown 对齐;流式未命名产物不再误请求 artifacts API,并兼容临时文件名路径。同时补强会话流中断识别与 8BF/岗位会商展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
acf538ac01
commit
3afa76894f
19 files changed
+2459
-1881
No files matched your search
@@ -36,6 +36,27 @@ EDITABLE_TEXT_SUFFIXES = {
|
||||
".log",
|
||||
}
|
||||
|
||||
_VIRTUAL_USER_DATA_PREFIX = "mnt/user-data"
|
||||
|
||||
|
||||
def coerce_artifact_virtual_path(path: str) -> str:
|
||||
"""Normalize an artifacts URL path to a sandbox virtual path.
|
||||
|
||||
Streaming write previews may request a bare filename such as
|
||||
``未命名文件.md`` before the model has emitted a real
|
||||
``/mnt/user-data/outputs/...`` path. Mapping a single path segment into
|
||||
the thread outputs directory turns that into a normal 404 (file not written
|
||||
yet) instead of HTTP 400 ``Path must start with /mnt/user-data``.
|
||||
"""
|
||||
stripped = path.strip().lstrip("/").replace("\\", "/")
|
||||
if not stripped:
|
||||
raise HTTPException(status_code=400, detail="Path must start with /mnt/user-data")
|
||||
if stripped == _VIRTUAL_USER_DATA_PREFIX or stripped.startswith(_VIRTUAL_USER_DATA_PREFIX + "/"):
|
||||
return stripped
|
||||
if "/" in stripped or stripped in {".", ".."}:
|
||||
raise HTTPException(status_code=400, detail="Path must start with /mnt/user-data")
|
||||
return f"{_VIRTUAL_USER_DATA_PREFIX}/outputs/{stripped}"
|
||||
|
||||
|
||||
class ArtifactUpdateRequest(BaseModel):
|
||||
content: str
|
||||
@@ -198,6 +219,7 @@ async def get_artifact(thread_id: str, path: str, request: Request, download: bo
|
||||
- Download file: `/api/threads/abc123/artifacts/mnt/user-data/outputs/data.csv?download=true`
|
||||
- Active web content such as `.html`, `.xhtml`, and `.svg` artifacts is always downloaded
|
||||
"""
|
||||
path = coerce_artifact_virtual_path(path)
|
||||
# Check if this is a request for a file inside a .skill archive (e.g., xxx.skill/SKILL.md)
|
||||
if ".skill/" in path:
|
||||
# Split the path at ".skill/" to get the ZIP file path and internal path
|
||||
@@ -285,6 +307,7 @@ async def update_artifact(
|
||||
if ".skill/" in path:
|
||||
raise HTTPException(status_code=400, detail="Skill archive contents are read-only.")
|
||||
|
||||
path = coerce_artifact_virtual_path(path)
|
||||
actual_path = await aresolve_thread_virtual_path(thread_id, path)
|
||||
logger.info(f"Updating artifact path: thread_id={thread_id}, requested_path={path}, actual_path={actual_path}")
|
||||
|
||||
|
||||
Reference in new issue
Block a user