diff --git a/src/muse/接入/__init__.py b/src/muse/接入/__init__.py
new file mode 100644
index 0000000..a4c13ca
--- /dev/null
+++ b/src/muse/接入/__init__.py
@@ -0,0 +1 @@
+"""身份与协议接入;业务行为由所属模块维护。"""
diff --git a/src/muse/接入/cli/__init__.py b/src/muse/接入/cli/__init__.py
new file mode 100644
index 0000000..d127de2
--- /dev/null
+++ b/src/muse/接入/cli/__init__.py
@@ -0,0 +1 @@
+"""本地具名命令接入。"""
diff --git a/src/muse/接入/cli/入口.py b/src/muse/接入/cli/入口.py
new file mode 100644
index 0000000..51a64b1
--- /dev/null
+++ b/src/muse/接入/cli/入口.py
@@ -0,0 +1,225 @@
+"""Python 命令入口。
+
+安装后 ``python -m muse`` 与 CLI(W07)使用一致的参数、退出码和业务结果。
+已实现子命令:自检(W02)、迁移(W03,显式维护命令,不在启动时自动执行)。
+未实现或未注入的能力明确失败,不返回伪成功。
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import sys
+
+from muse.共享.错误 import Muse错误
+from muse.启动 import 构建, 自检
+from muse.配置 import 读取配置
+
+_退出码_成功 = 0
+_退出码_用法错误 = 2
+_退出码_业务失败 = 1
+
+
+def _解析参数(argv: list[str] | None) -> argparse.Namespace:
+ 解析器 = argparse.ArgumentParser(
+ prog="python -m muse",
+ description="Muse 应用命令入口",
+ )
+ 子命令 = 解析器.add_subparsers(dest="命令", required=True)
+
+ 自检解析 = 子命令.add_parser("自检", help="报告装配状态;不连接外部服务")
+ 自检解析.add_argument("配置", help="应用配置 TOML 路径")
+
+ 迁移解析 = 子命令.add_parser("迁移", help="执行数据库迁移(显式维护命令)")
+ 迁移解析.add_argument("配置", help="应用配置 TOML 路径")
+ 迁移解析.add_argument("--目标版本", type=int, default=None, help="执行到该版本为止(默认全部)")
+ 服务解析 = 子命令.add_parser("服务", help="启动已配置的作者工作台")
+ 服务解析.add_argument("配置", help="应用配置 TOML 路径")
+ 任务解析 = 子命令.add_parser("任务", help="查看、暂停、取消或恢复持久任务")
+ 任务解析.add_argument("配置", help="作者配置 TOML 路径")
+ 任务解析.add_argument("动作", choices=["列表", "查看", "事件", "控制"])
+ 任务解析.add_argument("目标", nargs="?", help="任务 ID;控制时为具名请求 JSON 文件")
+ 任务解析.add_argument("--游标", type=int, default=0)
+ 结构解析 = 子命令.add_parser("结构", help="发布内置种子或查看已发布的固定结构")
+ 结构解析.add_argument("配置", help="应用配置 TOML 路径")
+ 结构解析.add_argument("动作", choices=["发布内置", "查看", "维护"])
+ 结构解析.add_argument("目标", nargs="?", help="结构身份")
+ 结构解析.add_argument("--版本", type=int, default=1)
+ 作品解析 = 子命令.add_parser("作品", help="管理作品档案、章节和人工正文版本")
+ 作品解析.add_argument("配置", help="作者配置 TOML 路径")
+ 作品解析.add_argument(
+ "动作",
+ choices=[
+ "结构",
+ "查看",
+ "目录",
+ "节点目录",
+ "保存节点",
+ "进度",
+ "正文",
+ "保存档案",
+ "新增章节",
+ "重排章节",
+ "保存正文",
+ "回退正文",
+ ],
+ )
+ 作品解析.add_argument("目标", help="查询身份或写入请求 JSON 文件")
+ 作品解析.add_argument("--结构", default="work_core")
+ 作品解析.add_argument("--版本", type=int, default=1)
+ 作品解析.add_argument("--分支", default="main")
+ 审阅解析 = 子命令.add_parser("审阅", help="比较候选、修改候选并提交作者决定")
+ 审阅解析.add_argument("配置", help="作者配置TOML路径")
+ 审阅解析.add_argument("动作", choices=["列表", "查看", "建立候选", "修改候选", "打开", "决定"])
+ 审阅解析.add_argument("目标", help="查询身份或操作请求JSON文件")
+ 审阅解析.add_argument("--分支", default="main")
+ 探索解析 = 子命令.add_parser("探索", help="保存私人想法与检查最终整理稿")
+ 探索解析.add_argument("配置", help="作者配置TOML路径")
+ 探索解析.add_argument("动作", choices=["列表", "查看", "保存", "检查", "选定", "查看选定"])
+ 探索解析.add_argument("目标", nargs="?", help="稿件身份或保存请求JSON文件")
+ 探索解析.add_argument("--预期哈希")
+ 规划解析 = 子命令.add_parser("规划", help="保存规划候选、查看版本并执行明确作者决定")
+ 规划解析.add_argument("配置")
+ 规划解析.add_argument(
+ "动作",
+ choices=[
+ "结构",
+ "列表",
+ "查看",
+ "创建候选",
+ "编辑候选",
+ "查看候选",
+ "打开审阅",
+ "决定",
+ "读取细纲",
+ ],
+ )
+ 规划解析.add_argument("目标")
+ 规划解析.add_argument("--结构", default="outline")
+ 规划解析.add_argument("--版本", type=int, default=1)
+ 规划解析.add_argument("--章节")
+ 配置解析 = 子命令.add_parser("配置", help="保存或查看运行配置草案;不自动验证启用")
+ 配置解析.add_argument("配置", help="应用连接配置TOML路径")
+ 配置解析.add_argument("动作", choices=["保存", "查看"])
+ 配置解析.add_argument("配置ID")
+ 配置解析.add_argument("版本")
+ 配置解析.add_argument("--内容", help="运行配置草案TOML路径")
+ 管理解析 = 子命令.add_parser("管理", help="管理当前配置的受控暂存;不访问其他根")
+ 管理解析.add_argument("配置", help="明确的应用连接配置")
+ 管理解析.add_argument("动作", choices=["清理无租约暂存", "查看清理回执"])
+ 管理解析.add_argument("对象ID", nargs="?")
+ return 解析器.parse_args(argv)
+
+
+def _运行迁移(参数: argparse.Namespace) -> int:
+ from muse.基础设施.数据库.迁移 import 迁移目标库
+
+ 已执行 = 迁移目标库(读取配置(参数.配置), 目标版本=参数.目标版本)
+ if 已执行:
+ for 项 in 已执行:
+ print(f"已应用 V{项.版本:04d} {项.文件名}")
+ else:
+ print("无待执行迁移(账本已是最新)")
+ return _退出码_成功
+
+
+def main(argv: list[str] | None = None) -> int:
+ 参数 = _解析参数(argv)
+ try:
+ if 参数.命令 == "规划":
+ from muse.接入.cli.作品命令 import 运行规划命令
+
+ 结果 = 运行规划命令(
+ 构建(读取配置(参数.配置)),
+ 参数.动作,
+ 参数.目标,
+ 结构ID=参数.结构,
+ 版本=参数.版本,
+ 章节ID=参数.章节,
+ )
+ print(json.dumps(结果, ensure_ascii=False))
+ return _退出码_成功
+ if 参数.命令 == "探索":
+ from muse.接入.cli.作品命令 import 运行探索命令
+
+ 结果 = 运行探索命令(
+ 构建(读取配置(参数.配置)), 参数.动作, 参数.目标, 预期哈希=参数.预期哈希
+ )
+ print(json.dumps(结果, ensure_ascii=False))
+ return (
+ _退出码_业务失败
+ if isinstance(结果, dict) and 结果.get("valid") is False
+ else _退出码_成功
+ )
+ if 参数.命令 == "审阅":
+ from muse.接入.cli.审阅命令 import 运行审阅命令
+
+ 结果 = 运行审阅命令(构建(读取配置(参数.配置)), 参数.动作, 参数.目标, 分支=参数.分支)
+ print(json.dumps(结果, ensure_ascii=False))
+ return _退出码_成功
+ if 参数.命令 == "管理":
+ from muse.接入.cli.管理命令 import 运行管理命令
+
+ 结果 = 运行管理命令(构建(读取配置(参数.配置)), 参数.动作, 参数.对象ID)
+ print(json.dumps(结果, ensure_ascii=False))
+ return _退出码_成功
+ if 参数.命令 == "配置":
+ from muse.接入.cli.管理命令 import 运行配置命令
+
+ 结果 = 运行配置命令(
+ 构建(读取配置(参数.配置)), 参数.动作, 参数.配置ID, 参数.版本, 参数.内容
+ )
+ print(json.dumps(结果, ensure_ascii=False))
+ return _退出码_成功
+ if 参数.命令 == "作品":
+ from muse.接入.cli.作品命令 import 运行作品命令
+
+ 结果 = 运行作品命令(
+ 构建(读取配置(参数.配置)),
+ 参数.动作,
+ 参数.目标,
+ 结构ID=参数.结构,
+ 版本=参数.版本,
+ 分支=参数.分支,
+ )
+ print(json.dumps(结果, ensure_ascii=False))
+ return _退出码_成功
+ if 参数.命令 == "结构":
+ from muse.接入.cli.结构命令 import 运行结构命令
+
+ 结果 = 运行结构命令(构建(读取配置(参数.配置)), 参数.动作, 参数.目标, 参数.版本)
+ print(json.dumps(结果, ensure_ascii=False))
+ return _退出码_成功
+ if 参数.命令 == "任务":
+ from muse.接入.cli.任务命令 import 运行任务命令
+
+ 结果 = 运行任务命令(构建(读取配置(参数.配置)), 参数.动作, 参数.目标, 参数.游标)
+ print(json.dumps(结果, ensure_ascii=False, default=str))
+ return _退出码_成功
+ if 参数.命令 == "自检":
+ 装配 = 构建(读取配置(参数.配置))
+ for 键, 值 in 自检(装配).items():
+ print(f"{键}: {值}")
+ return _退出码_成功
+ if 参数.命令 == "迁移":
+ return _运行迁移(参数)
+ if 参数.命令 == "服务":
+ import uvicorn
+
+ from muse.基础设施.可观测性 import 配置日志
+ from muse.接入.http.应用 import 创建应用
+
+ 配置 = 读取配置(参数.配置)
+ 应用 = 创建应用(配置)
+ 配置日志()
+ assert 配置.HTTP is not None
+ uvicorn.run(应用, host=配置.HTTP.监听地址, port=配置.HTTP.端口, access_log=False)
+ return _退出码_成功
+ except Muse错误 as exc:
+ print(json.dumps(exc.呈现(), ensure_ascii=False), file=sys.stderr)
+ return _退出码_业务失败
+ return _退出码_用法错误
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/src/muse/接入/http/__init__.py b/src/muse/接入/http/__init__.py
new file mode 100644
index 0000000..2352aa5
--- /dev/null
+++ b/src/muse/接入/http/__init__.py
@@ -0,0 +1 @@
+"""单作者 HTTP 接入。"""
diff --git a/src/muse/接入/http/事件订阅.py b/src/muse/接入/http/事件订阅.py
new file mode 100644
index 0000000..40cd53c
--- /dev/null
+++ b/src/muse/接入/http/事件订阅.py
@@ -0,0 +1,95 @@
+"""SSE 与有限事件页共用 PostgreSQL 持久化游标。"""
+
+import asyncio
+from datetime import datetime
+from typing import Any
+
+from fastapi import APIRouter, Header, Query, Request
+from fastapi.responses import StreamingResponse
+from pydantic import BaseModel
+from starlette.concurrency import run_in_threadpool
+
+from muse.任务运行.接口 import 事件续接
+from muse.接入.http.作者会话 import 作者依赖
+from muse.接入.http.依赖 import 取得任务服务, 读取作者任务
+
+路由 = APIRouter(prefix="/api/v1/tasks", tags=["任务事件"])
+
+
+class 事件响应(BaseModel):
+ event_id: str
+ sequence: int
+ event_type: str
+ occurred_at: datetime
+ payload: dict[str, Any]
+
+
+class 事件页(BaseModel):
+ items: list[事件响应]
+ reset_required: bool
+ first_sequence: int
+ last_sequence: int
+
+
+def 呈现事件(结果: 事件续接) -> 事件页:
+ return 事件页(
+ items=[
+ 事件响应(
+ event_id=项.事件ID,
+ sequence=项.序号,
+ event_type=项.类型.value,
+ occurred_at=项.发生时间,
+ payload=项.载荷,
+ )
+ for 项 in 结果.事件
+ ],
+ reset_required=结果.需要快照,
+ first_sequence=结果.可用首序号,
+ last_sequence=结果.最后序号,
+ )
+
+
+@路由.get("/{task_id}/events", response_model=事件页, operation_id="read_task_events")
+def 读取事件(
+ task_id: str,
+ request: Request,
+ 作者: 作者依赖,
+ cursor: int = Query(0, ge=0),
+ work_id: str | None = None,
+) -> 事件页:
+ 读取作者任务(request, task_id, work_id)
+ return 呈现事件(取得任务服务(request).续接事件(task_id, cursor))
+
+
+@路由.get("/{task_id}/events/stream", operation_id="stream_task_events")
+async def 订阅事件(
+ task_id: str,
+ request: Request,
+ 作者: 作者依赖,
+ cursor: int = Query(0, ge=0),
+ work_id: str | None = None,
+ last_event_id: int | None = Header(None, ge=0),
+):
+ await run_in_threadpool(读取作者任务, request, task_id, work_id)
+ 服务 = 取得任务服务(request)
+
+ async def 数据流():
+ 游标 = max(cursor, last_event_id or 0)
+ while not await request.is_disconnected():
+ 结果 = await run_in_threadpool(服务.续接事件, task_id, 游标)
+ 页 = 呈现事件(结果)
+ if 页.reset_required:
+ yield f"event: reset\ndata: {页.model_dump_json()}\n\n"
+ return
+ for 事件 in 页.items:
+ yield f"id: {事件.sequence}\nevent: task-event\ndata: {事件.model_dump_json()}\n\n"
+ 游标 = 事件.sequence
+ if not 页.items:
+ yield ": keep-alive\n\n"
+ await asyncio.sleep(1)
+
+ return StreamingResponse(
+ 数据流(),
+ media_type="text/event-stream",
+ headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
+ )
diff --git a/src/muse/接入/http/作者会话.py b/src/muse/接入/http/作者会话.py
new file mode 100644
index 0000000..9d3092a
--- /dev/null
+++ b/src/muse/接入/http/作者会话.py
@@ -0,0 +1,53 @@
+"""作者凭据只用于建立服务端签名会话,不接受客户端自报身份。"""
+
+import secrets
+from typing import Annotated
+
+from fastapi import APIRouter, Depends, HTTPException, Request
+from pydantic import BaseModel, ConfigDict, SecretStr
+
+from muse.配置 import 服务配置
+
+
+class 登录请求(BaseModel):
+ model_config = ConfigDict(extra="forbid")
+ password: SecretStr
+
+
+class 会话响应(BaseModel):
+ author_id: str
+
+
+def 要求作者(request: Request) -> str:
+ 配置: 服务配置 = request.app.state.服务配置
+ if request.session.get("author_id") != 配置.作者ID:
+ raise HTTPException(401, "请先连接作者工作台")
+ return 配置.作者ID
+
+
+作者依赖 = Annotated[str, Depends(要求作者)]
+
+
+def 会话路由() -> APIRouter:
+ 路由 = APIRouter(prefix="/api/v1/session", tags=["作者会话"])
+
+ @路由.post("", response_model=会话响应, operation_id="open_session")
+ def 登录(请求: 登录请求, request: Request) -> 会话响应:
+ 配置: 服务配置 = request.app.state.服务配置
+ 口令: str = request.app.state.口令
+ if not secrets.compare_digest(请求.password.get_secret_value().encode(), 口令.encode()):
+ raise HTTPException(401, "访问口令不正确")
+ request.session.clear()
+ request.session["author_id"] = 配置.作者ID
+ return 会话响应(author_id=配置.作者ID)
+
+ @路由.get("", response_model=会话响应, operation_id="read_session")
+ def 读取(作者: 作者依赖) -> 会话响应:
+ return 会话响应(author_id=作者)
+
+ @路由.delete("", operation_id="close_session")
+ def 退出(request: Request, 作者: 作者依赖) -> dict[str, bool]:
+ request.session.clear()
+ return {"closed": True}
+
+ return 路由
diff --git a/src/muse/接入/http/依赖.py b/src/muse/接入/http/依赖.py
new file mode 100644
index 0000000..ab8aef6
--- /dev/null
+++ b/src/muse/接入/http/依赖.py
@@ -0,0 +1,18 @@
+"""从已认证请求和应用装配取得任务能力,不接受客户端作者身份。"""
+
+from fastapi import HTTPException, Request
+
+from muse.任务运行.接口 import 任务快照, 任务服务
+from muse.接入.http.作者会话 import 要求作者
+
+
+def 取得任务服务(request: Request) -> 任务服务:
+ return request.app.state.装配.任务运行
+
+
+def 读取作者任务(request: Request, 任务ID: str, 作品ID: str | None = None) -> 任务快照:
+ 作者 = 要求作者(request)
+ 任务 = 取得任务服务(request).读取任务(任务ID)
+ if 任务.作者 != 作者 or (作品ID is not None and 任务.冻结输入.get("作品ID") != 作品ID):
+ raise HTTPException(403, "任务不属于当前作者或作品")
+ return 任务
diff --git a/src/muse/接入/http/健康检查.py b/src/muse/接入/http/健康检查.py
new file mode 100644
index 0000000..ef28fe8
--- /dev/null
+++ b/src/muse/接入/http/健康检查.py
@@ -0,0 +1,25 @@
+"""分别报告进程存活和依赖就绪,不推断业务任务成功。"""
+
+from fastapi import APIRouter, Request
+from fastapi.responses import JSONResponse
+
+from muse.共享.错误 import Muse错误, 资源缺失错误
+from muse.资源加载 import 核对资源
+
+路由 = APIRouter(tags=["系统状态"])
+
+
+@路由.get("/health", operation_id="read_health")
+def 存活() -> dict[str, str]:
+ return {"status": "alive"}
+
+
+@路由.get("/ready", operation_id="read_readiness")
+def 就绪(request: Request) -> JSONResponse:
+ try:
+ request.app.state.装配.要求数据库().检查连接()
+ if 核对资源()["发布身份"] != request.app.state.装配.配置.资源发布身份:
+ raise 资源缺失错误("应用配置与资源发布身份不一致")
+ except Muse错误 as exc:
+ return JSONResponse(status_code=503, content={"status": "unavailable", "code": exc.错误码})
+ return JSONResponse({"status": "ready"})
diff --git a/src/muse/接入/http/应用.py b/src/muse/接入/http/应用.py
new file mode 100644
index 0000000..c10008c
--- /dev/null
+++ b/src/muse/接入/http/应用.py
@@ -0,0 +1,94 @@
+"""装配 HTTP 生命周期、单作者会话、来源校验与具名业务路由。"""
+
+import hashlib
+from contextlib import asynccontextmanager
+from time import monotonic
+from uuid import uuid4
+
+from fastapi import FastAPI, Request
+from starlette.middleware.sessions import SessionMiddleware
+
+from muse.共享.错误 import 配置错误
+from muse.启动 import 构建
+from muse.基础设施.凭据读取 import 读取凭据
+from muse.基础设施.可观测性 import 记录运行
+from muse.接入.http.事件订阅 import 路由 as 事件路由
+from muse.接入.http.作者会话 import 会话路由
+from muse.接入.http.健康检查 import 路由 as 健康路由
+from muse.接入.http.路由.任务运行 import 路由 as 任务路由
+from muse.接入.http.路由.作品规划 import 路由 as 作品路由
+from muse.接入.http.路由.作者审阅 import 路由 as 审阅路由
+from muse.接入.http.路由.元数据 import 路由 as 元数据路由
+from muse.接入.http.路由.故事世界 import 路由 as 世界路由
+from muse.接入.http.路由.正文写作 import 路由 as 正文路由
+from muse.接入.http.错误响应 import 协议错误, 安装错误响应
+from muse.接入.http.静态资源 import 挂载工作台
+from muse.配置 import 应用配置
+
+
+def 创建应用(配置: 应用配置) -> FastAPI:
+ 服务 = 配置.HTTP
+ if 服务 is None:
+ raise 配置错误("启动工作台需要明确的 HTTP 与作者口令配置")
+ 口令 = 读取凭据("受控存储", 服务.口令文件)
+ 装配 = 构建(配置)
+
+ @asynccontextmanager
+ async def 生命周期(app: FastAPI):
+ app.state.装配 = 装配
+ app.state.服务配置 = 服务
+ yield
+
+ 应用 = FastAPI(
+ title="Muse 作者工作台", version="0.1.0", lifespan=生命周期, docs_url=None, redoc_url=None
+ )
+ 应用.state.口令 = 口令
+ 应用.state.装配 = 装配
+ 应用.state.服务配置 = 服务
+ 应用.add_middleware(
+ SessionMiddleware,
+ secret_key=hashlib.sha256(("muse-session:" + 口令).encode()).hexdigest(),
+ session_cookie="muse_session",
+ same_site="strict",
+ max_age=12 * 3600,
+ https_only=服务.公开地址.startswith("https://"),
+ )
+
+ @应用.middleware("http")
+ async def 写入来源(request: Request, call_next):
+ 开始 = monotonic()
+ 请求ID = str(uuid4())
+ request.state.request_id = 请求ID
+ if request.method not in {"GET", "HEAD", "OPTIONS"}:
+ if request.headers.get("origin") not in 服务.允许来源:
+ return 协议错误("SCOPE_DENIED", "写入请求的来源不在允许范围", 403)
+ 响应 = await call_next(request)
+ 响应.headers["X-Request-ID"] = 请求ID
+ 响应.headers["X-Content-Type-Options"] = "nosniff"
+ 记录运行("http.completed", 请求ID=请求ID, 状态=响应.status_code, 耗时秒=monotonic() - 开始)
+ return 响应
+
+ 安装错误响应(应用)
+ 注册API(应用)
+ 挂载工作台(应用)
+ return 应用
+
+
+def 注册API(应用: FastAPI) -> None:
+ """运行和合同生成共享同一组路由;不创建连接或加载凭据。"""
+ 应用.include_router(任务路由)
+ 应用.include_router(元数据路由)
+ 应用.include_router(作品路由)
+ 应用.include_router(正文路由)
+ 应用.include_router(审阅路由)
+ 应用.include_router(世界路由)
+ 应用.include_router(事件路由)
+ 应用.include_router(健康路由)
+ 应用.include_router(会话路由())
+
+
+def 导出合同() -> dict:
+ """生成静态外壳,动态作品结构由 S04 运行时提供。"""
+ 应用 = FastAPI(title="Muse 作者工作台", version="0.1.0")
+ 注册API(应用)
+ return 应用.openapi()
diff --git a/src/muse/接入/http/路由/__init__.py b/src/muse/接入/http/路由/__init__.py
new file mode 100644
index 0000000..853ef69
--- /dev/null
+++ b/src/muse/接入/http/路由/__init__.py
@@ -0,0 +1 @@
+"""具名业务 HTTP 路由。"""
diff --git a/src/muse/接入/http/错误响应.py b/src/muse/接入/http/错误响应.py
new file mode 100644
index 0000000..da8c919
--- /dev/null
+++ b/src/muse/接入/http/错误响应.py
@@ -0,0 +1,57 @@
+"""统一错误外壳;验证错误不回显请求值。"""
+
+from typing import Any
+
+from fastapi import FastAPI, Request
+from fastapi.exceptions import RequestValidationError
+from fastapi.responses import JSONResponse
+from starlette.exceptions import HTTPException
+
+from muse.共享.错误 import Muse错误
+from muse.接入.主会话 import 参数合同错误
+
+
+def 错误内容(错误: Muse错误, *, target_ref: str | None = None) -> dict[str, Any]:
+ return {**错误.呈现(), "target_ref": target_ref}
+
+
+def 协议错误(code: str, message: str, status: int, details: Any = None) -> JSONResponse:
+ return JSONResponse(
+ status_code=status,
+ content={
+ "code": code,
+ "message": message,
+ "target_ref": None,
+ "retryable": False,
+ "details": details or {},
+ "actions": [],
+ },
+ )
+
+
+def 安装错误响应(应用: FastAPI) -> None:
+ @应用.exception_handler(Muse错误)
+ async def 业务错误(request: Request, exc: Muse错误) -> JSONResponse:
+ code = exc.错误码
+ if code in {"SCOPE_DENIED", "MUSE_PURPOSE_VIOLATION"}:
+ status = 403
+ elif code.endswith("NOT_FOUND"):
+ status = 404
+ elif code.endswith(("STALE", "CONFLICT", "MISMATCH")):
+ status = 409
+ elif exc.可重试:
+ status = 503
+ else:
+ status = 422
+ return JSONResponse(status_code=status, content=错误内容(exc))
+
+ @应用.exception_handler(RequestValidationError)
+ async def 参数错误(request: Request, exc: RequestValidationError) -> JSONResponse:
+ return JSONResponse(status_code=422, content=参数合同错误(exc.errors()).呈现())
+
+ @应用.exception_handler(HTTPException)
+ async def 访问错误(request: Request, exc: HTTPException) -> JSONResponse:
+ code = {401: "UNAUTHENTICATED", 403: "SCOPE_DENIED", 404: "NOT_FOUND"}.get(
+ exc.status_code, "HTTP_ERROR"
+ )
+ return 协议错误(code, str(exc.detail), exc.status_code)
diff --git a/src/muse/接入/http/静态资源.py b/src/muse/接入/http/静态资源.py
new file mode 100644
index 0000000..9fe48be
--- /dev/null
+++ b/src/muse/接入/http/静态资源.py
@@ -0,0 +1,25 @@
+"""只提供已发布工作台资源,SPA 路由不接管 API 错误。"""
+
+import mimetypes
+from pathlib import PurePosixPath
+
+from fastapi import FastAPI, HTTPException, Response
+
+from muse.共享.错误 import 资源缺失错误
+from muse.资源加载 import 打开资源
+
+
+def 挂载工作台(应用: FastAPI) -> None:
+ @应用.get("/{path:path}", include_in_schema=False)
+ def 工作台文件(path: str) -> Response:
+ if path.startswith("api/"):
+ raise HTTPException(404, "接口不存在")
+ 名称 = path if PurePosixPath(path).suffix else "index.html"
+ try:
+ 数据 = 打开资源(f"工作台/{名称}")
+ except 资源缺失错误:
+ raise HTTPException(404, "工作台资源不存在,请核对安装包") from None
+ 缓存 = "no-cache" if 名称 == "index.html" else "public, max-age=31536000, immutable"
+ return Response(
+ 数据, media_type=mimetypes.guess_type(名称)[0], headers={"Cache-Control": 缓存}
+ )
diff --git a/src/muse/接入/主会话.py b/src/muse/接入/主会话.py
new file mode 100644
index 0000000..a1f8344
--- /dev/null
+++ b/src/muse/接入/主会话.py
@@ -0,0 +1,58 @@
+"""主会话、HTTP 和 CLI 共用的具名任务控制输入与转交。"""
+
+from typing import Literal
+
+from pydantic import BaseModel, ConfigDict, Field
+
+from muse.任务运行.接口 import 任务服务, 任务状态
+from muse.元数据.接口 import 有效结构, 生成结构合同
+from muse.共享.错误 import Muse错误
+
+
+def 参数合同错误(字段错误, *, 前缀: tuple[str, ...] = ()) -> Muse错误:
+ 错误 = Muse错误(
+ "请求字段不符合接口合同",
+ 上下文={"fields": [{"field": [*前缀, *e["loc"]], "type": e["type"]} for e in 字段错误]},
+ )
+ 错误.错误码 = "INVALID_REQUEST"
+ return 错误
+
+
+class 结构读取结果(BaseModel):
+ schema_id: str
+ schema_version: int
+ type_id: str
+ effective_schema_hash: str
+ contract: dict
+
+
+def 呈现结构(结构: 有效结构) -> 结构读取结果:
+ return 结构读取结果(
+ schema_id=结构.基础.schema_id,
+ schema_version=结构.基础.schema_version,
+ type_id=结构.基础.type_id,
+ effective_schema_hash=结构.effective_schema_hash,
+ contract=生成结构合同(结构),
+ )
+
+
+class 任务控制请求(BaseModel):
+ model_config = ConfigDict(extra="forbid")
+ command_id: str = Field(min_length=1)
+ target_ref: str = Field(min_length=1)
+ expected_state: 任务状态
+ action: Literal["暂停", "取消", "恢复"]
+
+
+class 任务控制回执(BaseModel):
+ command_id: str
+ task_id: str
+ state: 任务状态
+ last_sequence: int
+
+
+def 执行任务控制(服务: 任务服务, 作者: str, 请求: 任务控制请求) -> 任务控制回执:
+ 回执 = 服务.控制任务(
+ 请求.target_ref, 作者, 请求.expected_state, 请求.action, 命令ID=请求.command_id
+ )
+ return 任务控制回执.model_validate(回执)
diff --git a/tests/集成/test_HTTP任务接入.py b/tests/集成/test_HTTP任务接入.py
new file mode 100644
index 0000000..2f2d6b4
--- /dev/null
+++ b/tests/集成/test_HTTP任务接入.py
@@ -0,0 +1,189 @@
+"""真实 ASGI、CLI 与隔离 PostgreSQL 共用任务合同;不使用仓储替身。"""
+
+import json
+import subprocess
+import sys
+from pathlib import Path
+
+import pytest
+from fastapi.testclient import TestClient
+
+from muse.任务运行.接口 import 任务服务, 任务请求, 步骤处理器, 步骤结果, 步骤计划
+from muse.共享.调用身份 import 内容用途, 用途
+from muse.接入.http.应用 import 创建应用
+from muse.编排.接口 import 流程定义, 流程服务, 流程登记
+from muse.配置 import 应用配置, 服务配置
+
+pytestmark = pytest.mark.数据库
+
+
+def test_具名CLI按字段传参不依赖JSON排列__793c2a(任务接入环境, tmp_path: Path) -> None:
+ 服务, 身份, _, 配置文件 = 任务接入环境
+ 请求文件 = tmp_path / "作者决定.json"
+ 请求文件.write_text(
+ json.dumps(
+ {
+ "action": "取消",
+ "expected_state": "queued",
+ "target_ref": 身份,
+ "command_id": "作者决定-1",
+ },
+ ensure_ascii=False,
+ )
+ )
+ 进程 = subprocess.run(
+ [sys.executable, "-m", "muse", "任务", str(配置文件), "控制", str(请求文件)],
+ cwd="/",
+ capture_output=True,
+ text=True,
+ timeout=20,
+ )
+ assert 进程.returncode == 0, 进程.stderr
+ assert json.loads(进程.stdout)["command_id"] == "作者决定-1"
+ assert 服务.读取任务(身份).状态.value == "cancelled"
+
+
+@pytest.mark.parametrize(
+ "请求文本,额外参数,退出码",
+ [
+ ("{}", [], 1),
+ ('["x", 1, null]', [], 1),
+ ("{}", ["--stdin"], 2),
+ ("not json", [], 1),
+ ('{"a": 1}', [], 1),
+ ('{"sql": "CREATE TABLE t (id int)"}', [], 1),
+ ('{"sql": "DELETE FROM t"}', [], 1),
+ ('{"sql": "ALTER TABLE t ADD COLUMN x int", "params": []}', [], 1),
+ ('{"sql": "DELETE FROM t", "params": []}', [], 1),
+ ('{"sql": "UPDATE t SET x=1 WHERE id=1"}', [], 1),
+ ('{"sql": "DELETE FROM t WHERE id=%s", "params": [1]}', [], 1),
+ ],
+ ids=[
+ "empty",
+ "typed-array",
+ "competing-input",
+ "invalid-json",
+ "unknown-object",
+ "create-sql",
+ "bare-delete",
+ "alter-params",
+ "delete-params",
+ "update-where",
+ "delete-where",
+ ],
+)
+def test_具名CLI拒绝未登记输入且任务不变__a71002(
+ 任务接入环境,
+ tmp_path: Path,
+ 请求文本: str,
+ 额外参数: list[str],
+ 退出码: int,
+) -> None:
+ """旧通用 SQL 入口已退出;只接受具名命令合同,不能以 WHERE 或参数列表换取写能力。"""
+ 服务, 身份, _, 配置文件 = 任务接入环境
+ 请求文件 = tmp_path / "请求.json"
+ 请求文件.write_text(请求文本)
+ 进程 = subprocess.run(
+ [sys.executable, "-m", "muse", "任务", str(配置文件), "控制", str(请求文件), *额外参数],
+ cwd="/",
+ capture_output=True,
+ text=True,
+ timeout=20,
+ )
+ assert 进程.returncode == 退出码, 进程.stdout
+ if 退出码 == 1:
+ assert json.loads(进程.stderr)["code"] == "INVALID_REQUEST"
+ assert 服务.读取任务(身份).状态.value == "queued"
+
+
+@pytest.fixture
+def 任务接入环境(应用测试库, tmp_path: Path):
+ registry = 流程登记()
+ registry.登记处理器(步骤处理器("synthetic", "1", lambda _: 步骤结果({"ok": True}), "v1", "v1"))
+ registry.登记类型("synthetic", 必需保护=())
+ service = 任务服务(应用测试库[用途.生产], registry)
+ flow = 流程服务(service, registry)
+ flow.发布(流程定义("synthetic", "1", (步骤计划("合成步骤", "synthetic", "1"),)))
+ req = 任务请求(
+ "合成任务",
+ "create",
+ "author-local",
+ 用途.生产,
+ 内容用途.抽取,
+ {},
+ "v1",
+ "test",
+ {
+ "source_scope": {},
+ "schema_versions": {},
+ "authorization": "synthetic",
+ "budget": {},
+ "stop_conditions": [],
+ },
+ 作品ID="work-A",
+ )
+ task_id = flow.发起(req, "synthetic", "1")
+ token = tmp_path / "口令.txt"
+ token.write_text("synthetic-password")
+ config = 应用配置(
+ 应用测试库[用途.生产].引用,
+ "test",
+ HTTP=服务配置(str(token), 公开地址="http://testserver", 允许来源=("http://testserver",)),
+ )
+ file = tmp_path / "应用.toml"
+ file.write_text(f"""["数据库"]
+"取值方式" = "受控存储"
+"位置" = {json.dumps(str(tmp_path / "production.txt"))}
+["资源"]
+"发布身份" = "test"
+[HTTP]
+"口令文件" = {json.dumps(str(token))}
+""")
+ return service, task_id, config, file
+
+
+def test_HTTP与CLI控制回执和拒绝保持一致__a71001(任务接入环境, tmp_path: Path) -> None:
+ service, task_id, config, file = 任务接入环境
+ with TestClient(创建应用(config)) as client:
+ assert client.get(f"/api/v1/tasks/{task_id}").status_code == 401
+ client.headers["Origin"] = "http://testserver"
+ assert (
+ client.post("/api/v1/session", json={"password": "synthetic-password"}).status_code
+ == 200
+ )
+ assert (
+ client.get("/api/v1/tasks", params={"work_id": "work-A"}).json()[0]["task_id"]
+ == task_id
+ )
+ assert (
+ client.get(f"/api/v1/tasks/{task_id}", params={"work_id": "work-B"}).status_code == 403
+ )
+ body = {
+ "command_id": "cancel-once",
+ "target_ref": task_id,
+ "expected_state": "queued",
+ "action": "取消",
+ }
+ response = client.post(f"/api/v1/tasks/{task_id}/controls", json=body)
+ assert response.status_code == 200
+ request = tmp_path / "请求.json"
+ request.write_text(json.dumps(body))
+ args = [sys.executable, "-m", "muse", "任务", str(file), "控制", str(request)]
+ cli = subprocess.run(args, cwd="/", capture_output=True, text=True, timeout=20)
+ assert cli.returncode == 0, cli.stderr
+ assert json.loads(cli.stdout) == response.json()
+ body["action"] = "暂停"
+ request.write_text(json.dumps(body))
+ response = client.post(f"/api/v1/tasks/{task_id}/controls", json=body)
+ cli = subprocess.run(args, cwd="/", capture_output=True, text=True, timeout=20)
+ assert response.status_code == 409 and cli.returncode == 1
+ assert response.json() == json.loads(cli.stderr)
+ events = client.get(f"/api/v1/tasks/{task_id}/events").json()
+ assert [event["event_type"] for event in events["items"]] == [
+ "task.created",
+ "task.cancelled",
+ ]
+ assert (
+ client.get(f"/api/v1/tasks/{task_id}/events", params={"cursor": 2}).json()["items"]
+ == []
+ )
diff --git a/web/src/main.tsx b/web/src/main.tsx
new file mode 100644
index 0000000..4bbba2b
--- /dev/null
+++ b/web/src/main.tsx
@@ -0,0 +1,14 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import { Ui应用 } from "./应用/应用";
+import { Ui依赖装配 } from "./应用/依赖装配";
+import "./界面/主题.css";
+import "./界面/排版.css";
+
+ReactDOM.createRoot(document.getElementById("root")!).render(
+
+
+
+
+ ,
+);
diff --git a/web/src/vite-env.d.ts b/web/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/web/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/web/src/功能/任务运行/index.ts b/web/src/功能/任务运行/index.ts
new file mode 100644
index 0000000..2dba094
--- /dev/null
+++ b/web/src/功能/任务运行/index.ts
@@ -0,0 +1 @@
+export { Ui任务列表 } from "./任务列表";
diff --git a/web/src/功能/任务运行/任务列表.tsx b/web/src/功能/任务运行/任务列表.tsx
new file mode 100644
index 0000000..accd1a3
--- /dev/null
+++ b/web/src/功能/任务运行/任务列表.tsx
@@ -0,0 +1,148 @@
+import { useState, type FormEvent } from "react";
+import { useQuery } from "@tanstack/react-query";
+import { 客户端 } from "../../接口/生成/客户端";
+import { Ui反馈状态 } from "../../界面/反馈状态";
+import { useMuse作品会话 } from "../../应用/作品会话";
+import { 查询键 } from "../../接口/查询键";
+import { 状态 } from "./数据";
+import { Ui任务详情 } from "./任务详情";
+
+export function Ui任务列表() {
+ const work = useMuse作品会话();
+ const allTasks = useQuery({
+ queryKey: ["任务总览"],
+ queryFn: async ({ signal }) =>
+ (await 客户端.GET("/api/v1/tasks", { signal })).data!,
+ });
+ const works = [
+ ...new Set(
+ (allTasks.data ?? []).flatMap((item) =>
+ item.work_id ? [item.work_id] : [],
+ ),
+ ),
+ ];
+ const list = useQuery({
+ queryKey: 查询键(work.workId, "任务列表"),
+ queryFn: async ({ signal }) =>
+ (
+ await 客户端.GET("/api/v1/tasks", {
+ params: { query: { work_id: work.workId ?? undefined } },
+ signal,
+ })
+ ).data!,
+ });
+ const [input, setInput] = useState("");
+ const [taskId, setTaskId] = useState("");
+ const task = useQuery({
+ queryKey: 查询键(work.workId, taskId),
+ enabled: !!taskId,
+ queryFn: async ({ signal }) =>
+ (
+ await 客户端.GET("/api/v1/tasks/{task_id}", {
+ params: {
+ path: { task_id: taskId },
+ query: { work_id: work.workId ?? undefined },
+ },
+ signal,
+ })
+ ).data!,
+ });
+ function find(event: FormEvent) {
+ event.preventDefault();
+ setTaskId(input.trim());
+ }
+ return (
+
+
+
+
创作进程
+
每一步,都可以接续
+
查看已发起的任务,跟进进度与处理结果。
+
+
01 / 任务记录
+
+
+
+
+
+
+ {!taskId ? (
+
+
+
最近任务
+ {list.data?.length ?? 0} 条记录
+
+ {list.isPending ? (
+
+ ) : list.error ? (
+
+ ) : list.data?.length ? (
+
+ {list.data.map((item) => (
+ -
+
+
+ ))}
+
+ ) : (
+
+
这里会保留每一步进度
+
当前范围还没有任务记录。
+
+ )}
+
+ ) : task.isPending ? (
+
+ ) : task.error ? (
+
+ ) : task.data ? (
+
+ ) : null}
+
+ );
+}
diff --git a/web/src/功能/任务运行/任务详情.tsx b/web/src/功能/任务运行/任务详情.tsx
new file mode 100644
index 0000000..8d2bc6a
--- /dev/null
+++ b/web/src/功能/任务运行/任务详情.tsx
@@ -0,0 +1,79 @@
+import { useEffect, useState } from "react";
+import { useQueryClient } from "@tanstack/react-query";
+import { Ui恢复控制 } from "./恢复控制";
+import { 订阅任务事件 } from "../../接口/事件连接";
+import { 查询键 } from "../../接口/查询键";
+import { Ui反馈状态 } from "../../界面/反馈状态";
+import { 状态, 事件名, type 任务, type 任务事件 } from "./数据";
+
+export function Ui任务详情({
+ task,
+ scope,
+}: {
+ task: 任务;
+ scope: string | null;
+}) {
+ const cache = useQueryClient();
+ const [events, setEvents] = useState<任务事件[]>([]);
+ const [notice, setNotice] = useState("");
+ useEffect(
+ () =>
+ 订阅任务事件(task.task_id, {
+ 收到: (事件) => {
+ setEvents((旧) => [...旧, 事件].slice(-40));
+ setNotice("");
+ void cache.invalidateQueries({
+ queryKey: 查询键(scope, task.task_id),
+ });
+ },
+ 缺口: () => setNotice("较早的事件已归档,请重新读取任务进度。"),
+ 重连: () => setNotice("连接正在恢复,任务会继续保留。"),
+ }),
+ [task.task_id, scope, cache],
+ );
+ return (
+
+
+
+
任务进度
+ {状态[task.state]}
+
+ {task.task_id}
+
+
+ {task.steps.map((step, index) => (
+ -
+
+ {String(index + 1).padStart(2, "0")}
+
+
+ {step.step_id}
+ {状态[step.state] ?? step.state}
+
+
+ ))}
+
+
+
+
+
过程记录
+ 实时更新
+
+ {notice && }
+
+ {events.map((event) => (
+ -
+
+ {事件名[event.event_type] ?? "进度更新"}
+
+ ))}
+
+
+
+ );
+}
diff --git a/web/src/功能/任务运行/恢复控制.tsx b/web/src/功能/任务运行/恢复控制.tsx
new file mode 100644
index 0000000..062a8db
--- /dev/null
+++ b/web/src/功能/任务运行/恢复控制.tsx
@@ -0,0 +1,65 @@
+import { useRef, useState } from "react";
+import { useQueryClient } from "@tanstack/react-query";
+import { 客户端 } from "../../接口/生成/客户端";
+import { Ui反馈状态 } from "../../界面/反馈状态";
+import type { 任务 } from "./数据";
+
+export function Ui恢复控制({ task }: { task: 任务 }) {
+ const cache = useQueryClient();
+ const [busy, setBusy] = useState(false);
+ const [controlError, setControlError] = useState("");
+ const pending = useRef<{
+ action: "暂停" | "取消" | "恢复";
+ id: string;
+ } | null>(null);
+ async function 控制(action: "暂停" | "取消" | "恢复") {
+ if (pending.current?.action !== action)
+ pending.current = { action, id: crypto.randomUUID() };
+ setBusy(true);
+ setControlError("");
+ try {
+ await 客户端.POST("/api/v1/tasks/{task_id}/controls", {
+ params: { path: { task_id: task.task_id } },
+ body: {
+ command_id: pending.current.id,
+ target_ref: task.task_id,
+ expected_state: task.state,
+ action,
+ },
+ });
+ pending.current = null;
+ await cache.invalidateQueries();
+ } catch (e) {
+ setControlError(e instanceof Error ? e.message : "控制请求未完成。");
+ } finally {
+ setBusy(false);
+ }
+ }
+ return (
+ <>
+ {(["queued", "running"] as string[]).includes(task.state) && (
+
+
+
+
+ )}
+ {(["paused", "failed", "reconciling"] as string[]).includes(
+ task.state,
+ ) && (
+
+
+
+
+ )}
+ {controlError && }
+ >
+ );
+}
diff --git a/web/src/功能/任务运行/数据.ts b/web/src/功能/任务运行/数据.ts
new file mode 100644
index 0000000..7d9d616
--- /dev/null
+++ b/web/src/功能/任务运行/数据.ts
@@ -0,0 +1,29 @@
+import type { operations } from "../../接口/生成/类型";
+
+export type 任务 =
+ operations["read_task"]["responses"][200]["content"]["application/json"];
+export type { 任务事件 } from "../../接口/事件连接";
+export const 状态: Record = {
+ queued: "等待运行",
+ running: "正在进行",
+ paused: "已暂停",
+ reconciling: "等待核对",
+ failed: "未能完成",
+ cancelled: "已取消",
+ completed: "已完成",
+ pending: "待开始",
+ unknown: "等待核对",
+};
+export const 事件名: Record = {
+ "task.created": "任务已建立",
+ "step.started": "开始处理",
+ "step.completed": "步骤完成",
+ "task.completed": "任务完成",
+ "task.cancelled": "任务已取消",
+ "task.paused": "任务已暂停",
+ "task.resumed": "继续处理",
+ "step.failed": "步骤未完成",
+ "text.delta": "正文更新",
+ "step.checkpoint": "进度已保存",
+ "call.unknown": "等待结果核对",
+};
diff --git a/web/src/应用/作品会话.ts b/web/src/应用/作品会话.ts
new file mode 100644
index 0000000..1b8a59e
--- /dev/null
+++ b/web/src/应用/作品会话.ts
@@ -0,0 +1,13 @@
+import { useState } from "react";
+import { useQueryClient } from "@tanstack/react-query";
+
+/** 缓存身份和取消共用 React Query,避免维护第二套请求代次。 */
+export function useMuse作品会话() {
+ const [workId, setWorkId] = useState(null);
+ const cache = useQueryClient();
+ function change(next: string | null) {
+ void cache.cancelQueries({ queryKey: ["作品", workId] });
+ setWorkId(next);
+ }
+ return { workId, change };
+}
diff --git a/web/src/应用/作者会话.ts b/web/src/应用/作者会话.ts
new file mode 100644
index 0000000..88c6b23
--- /dev/null
+++ b/web/src/应用/作者会话.ts
@@ -0,0 +1,9 @@
+import { useQuery } from "@tanstack/react-query";
+import { 客户端 } from "../接口/生成/客户端";
+
+export function useMuse作者会话() {
+ return useQuery({
+ queryKey: ["作者会话"],
+ queryFn: async () => (await 客户端.GET("/api/v1/session")).data!,
+ });
+}
diff --git a/web/src/应用/依赖装配.tsx b/web/src/应用/依赖装配.tsx
new file mode 100644
index 0000000..806b5e8
--- /dev/null
+++ b/web/src/应用/依赖装配.tsx
@@ -0,0 +1,14 @@
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { useState, type PropsWithChildren } from "react";
+
+export function Ui依赖装配({ children }: PropsWithChildren) {
+ const [client] = useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: { retry: false, refetchOnWindowFocus: false },
+ },
+ }),
+ );
+ return {children};
+}
diff --git a/web/src/应用/工作台布局.tsx b/web/src/应用/工作台布局.tsx
new file mode 100644
index 0000000..0694587
--- /dev/null
+++ b/web/src/应用/工作台布局.tsx
@@ -0,0 +1,62 @@
+import { useState, type PropsWithChildren } from "react";
+import { useQueryClient } from "@tanstack/react-query";
+import { NavLink } from "react-router-dom";
+import { 客户端 } from "../接口/生成/客户端";
+import { Ui反馈状态 } from "../界面/反馈状态";
+
+export function Ui工作台布局({ children }: PropsWithChildren) {
+ const cache = useQueryClient();
+ const [disconnectError, setDisconnectError] = useState("");
+ async function 断开() {
+ try {
+ await 客户端.DELETE("/api/v1/session");
+ await cache.cancelQueries();
+ cache.setQueryData(["作者会话"], null);
+ cache.removeQueries({
+ predicate: (query) => query.queryKey[0] !== "作者会话",
+ });
+ } catch (e) {
+ setDisconnectError(e instanceof Error ? e.message : "断开请求未完成。");
+ }
+ }
+ return (
+
+
+
+
+ {disconnectError && }
+ {children}
+
+
+ );
+}
diff --git a/web/src/应用/应用.tsx b/web/src/应用/应用.tsx
new file mode 100644
index 0000000..3eb9068
--- /dev/null
+++ b/web/src/应用/应用.tsx
@@ -0,0 +1,96 @@
+import { useState, type FormEvent } from "react";
+import { useQueryClient } from "@tanstack/react-query";
+import { BrowserRouter } from "react-router-dom";
+import { 客户端 } from "../接口/生成/客户端";
+import { 接口错误 } from "../接口/传输";
+import { Ui反馈状态 } from "../界面/反馈状态";
+import { useMuse作者会话 } from "./作者会话";
+import { Ui工作台布局 } from "./工作台布局";
+import { Ui页面路由 } from "./路由";
+
+function Ui连接表单() {
+ const cache = useQueryClient();
+ const [password, setPassword] = useState("");
+ const [busy, setBusy] = useState(false);
+ const [error, setError] = useState("");
+ async function 连接(event: FormEvent) {
+ event.preventDefault();
+ setBusy(true);
+ setError("");
+ try {
+ const result = await 客户端.POST("/api/v1/session", {
+ body: { password },
+ });
+ setPassword("");
+ cache.setQueryData(["作者会话"], result.data);
+ } catch (e) {
+ setError(e instanceof Error ? e.message : "连接未完成。");
+ } finally {
+ setBusy(false);
+ }
+ }
+ return (
+
+
+
M
+
MUSE · 创作工作台
+
+ 把故事
+
+ 接着写下去。
+
+
+ 整理想法,守住每一次选择。
+
+ 从你的工作台继续。
+
+
+
+
+ );
+}
+
+export function Ui应用() {
+ const session = useMuse作者会话();
+ if (session.isPending)
+ return (
+
+
+
+ );
+ if (
+ session.error &&
+ !(session.error instanceof 接口错误 && session.error.status === 401)
+ )
+ return (
+
+
+
+
+ );
+ if (!session.data) return ;
+ return (
+
+
+
+
+
+ );
+}
diff --git a/web/src/应用/路由.tsx b/web/src/应用/路由.tsx
new file mode 100644
index 0000000..03f84bb
--- /dev/null
+++ b/web/src/应用/路由.tsx
@@ -0,0 +1,59 @@
+import { useState } from "react";
+import { Navigate, Route, Routes } from "react-router-dom";
+import { Ui任务列表 } from "../功能/任务运行";
+import {
+ Ui作品首页,
+ Ui作品档案,
+ Ui探索工作区,
+ Ui规划工作区,
+} from "../功能/作品规划";
+import { Ui候选审阅页, Ui正文页 } from "../功能/正文写作";
+import { 客户端 } from "../接口/生成/客户端";
+
+export function Ui页面路由() {
+ return (
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ }
+ />
+ } />
+ } />
+ } />
+
+ );
+}
+
+function Ui连接状态() {
+ const [message, setMessage] = useState("查看工作台当前的连接情况。");
+ const [busy, setBusy] = useState(false);
+ async function 检查() {
+ setBusy(true);
+ try {
+ await 客户端.GET("/ready");
+ setMessage("连接正常,工作台已就绪。");
+ } catch (e) {
+ setMessage(e instanceof Error ? e.message : "连接暂不可用。");
+ } finally {
+ setBusy(false);
+ }
+ }
+ return (
+
+ 工作台
+ 连接状态
+
+ 服务连接
+ {message}
+
+
+
+ );
+}
diff --git a/web/src/接口/事件连接.ts b/web/src/接口/事件连接.ts
new file mode 100644
index 0000000..1b2abad
--- /dev/null
+++ b/web/src/接口/事件连接.ts
@@ -0,0 +1,34 @@
+import type { operations } from "./生成/类型";
+
+export type 任务事件 =
+ operations["read_task_events"]["responses"][200]["content"]["application/json"]["items"][number];
+
+/** 原生 EventSource 携带 Last-Event-ID 重连;页面离开时显式关闭。 */
+export function 订阅任务事件(
+ 任务ID: string,
+ 回调: { 收到: (事件: 任务事件) => void; 缺口: () => void; 重连: () => void },
+) {
+ const 连接 = new EventSource(
+ `/api/v1/tasks/${encodeURIComponent(任务ID)}/events/stream`,
+ );
+ let 活跃 = true;
+ let 最后序号 = 0;
+ 连接.addEventListener("task-event", (消息) => {
+ if (!活跃) return;
+ const 事件 = JSON.parse((消息 as MessageEvent).data) as 任务事件;
+ if (事件.sequence <= 最后序号) return;
+ 最后序号 = 事件.sequence;
+ 回调.收到(事件);
+ });
+ 连接.addEventListener("reset", () => {
+ if (活跃) 回调.缺口();
+ 连接.close();
+ });
+ 连接.onerror = () => {
+ if (活跃) 回调.重连();
+ };
+ return () => {
+ 活跃 = false;
+ 连接.close();
+ };
+}
diff --git a/web/src/接口/传输.ts b/web/src/接口/传输.ts
new file mode 100644
index 0000000..da181eb
--- /dev/null
+++ b/web/src/接口/传输.ts
@@ -0,0 +1,34 @@
+/** 统一凭据、取消和错误;服务器错误保留稳定代码与中文说明。 */
+export class 接口错误 extends Error {
+ constructor(
+ public code: string,
+ message: string,
+ public status: number,
+ ) {
+ super(message);
+ }
+}
+
+export async function 传输(request: Request): Promise {
+ let response: Response;
+ try {
+ response = await fetch(request);
+ } catch (error) {
+ if (error instanceof DOMException && error.name === "AbortError")
+ throw error;
+ throw new 接口错误(
+ "NETWORK_UNAVAILABLE",
+ "暂时无法连接工作台,请稍后重试。",
+ 0,
+ );
+ }
+ if (!response.ok) {
+ const body = await response.json().catch(() => ({}));
+ throw new 接口错误(
+ body.code ?? "HTTP_ERROR",
+ body.message ?? "请求未完成,请检查连接后重试。",
+ response.status,
+ );
+ }
+ return response;
+}
diff --git a/web/src/接口/查询键.ts b/web/src/接口/查询键.ts
new file mode 100644
index 0000000..918c4a0
--- /dev/null
+++ b/web/src/接口/查询键.ts
@@ -0,0 +1,9 @@
+/** 查询身份包含作品和目标,切换作品不会共用同一条缓存。 */
+export function 查询键(
+ 作品: string | null,
+ 目标: string,
+ 版本: string = "current",
+ 用途: string = "production",
+) {
+ return ["作品", 作品, 目标, 版本, 用途] as const;
+}
diff --git a/web/src/接口/生成/客户端.ts b/web/src/接口/生成/客户端.ts
new file mode 100644
index 0000000..5e95dc0
--- /dev/null
+++ b/web/src/接口/生成/客户端.ts
@@ -0,0 +1,9 @@
+// 由工具/生成接口.py生成;稳定外壳来自同一OpenAPI。
+import createClient from "openapi-fetch";
+import type { paths } from "./类型";
+import { 传输 } from "../传输";
+
+export const 客户端 = createClient({
+ baseUrl: globalThis.location.origin, fetch: 传输,
+ credentials: "same-origin"
+});
diff --git a/web/src/接口/生成/类型.ts b/web/src/接口/生成/类型.ts
new file mode 100644
index 0000000..968b95e
--- /dev/null
+++ b/web/src/接口/生成/类型.ts
@@ -0,0 +1,4499 @@
+/**
+ * This file was auto-generated by openapi-typescript.
+ * Do not make direct changes to the file.
+ */
+
+export interface paths {
+ "/api/v1/tasks/{task_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取任务 */
+ get: operations["read_task"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 列出任务 */
+ get: operations["list_tasks"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/controls": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 控制任务 */
+ post: operations["control_task"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/role-session-authorizations": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 批准角色会话 */
+ post: operations["approve_role_session"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/raw-authorizations/{authorization_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post?: never;
+ /** 撤销原文批准 */
+ delete: operations["revoke_raw_retention"];
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/raw-authorizations": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 批准原文 */
+ post: operations["approve_raw_retention"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/raw-leases": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 创建原文租约 */
+ post: operations["create_raw_lease"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/raw-leases/{lease_id}/content/{content_hash}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ /** 写入原文 */
+ put: operations["write_raw_bytes"];
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/raw-leases/{lease_id}/archive": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 归档原文 */
+ post: operations["archive_raw_bytes"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/raw-leases/{lease_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post?: never;
+ /** 清理原文 */
+ delete: operations["cleanup_raw_lease"];
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/raw-recovery": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 恢复原文 */
+ post: operations["recover_raw_leases"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/evidence/{evidence_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取证据回执 */
+ get: operations["read_evidence_receipt"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/evidence/{evidence_id}/content": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ /** 补交证据 */
+ put: operations["backfill_evidence_bytes"];
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/schemas/commands": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 维护结构 */
+ post: operations["schema_command"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/schemas/{schema_id}/versions/{version}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取结构 */
+ get: operations["read_schema"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/plan-nodes": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取规划节点 */
+ get: operations["read_plan_nodes"];
+ put?: never;
+ /** 保存规划节点 */
+ post: operations["save_plan_nodes"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/progress": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取进度 */
+ get: operations["read_work_progress"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 列出作品 */
+ get: operations["list_works"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/form": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取表单 */
+ get: operations["read_work_form"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/profile": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 保存档案 */
+ post: operations["save_work_profile"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/chapters": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取目录 */
+ get: operations["read_work_chapters"];
+ put?: never;
+ /** 新增章节 */
+ post: operations["append_chapter"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/chapters/reorder": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 重排章节 */
+ post: operations["reorder_chapters"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/explorations": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 列出探索 */
+ get: operations["list_explorations"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/explorations/{draft_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取探索 */
+ get: operations["read_exploration"];
+ put?: never;
+ /** 保存探索 */
+ post: operations["save_exploration"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/explorations/{draft_id}/check": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 检查探索 */
+ post: operations["check_exploration_handoff"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/exploration-selection": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取选定 */
+ get: operations["read_exploration_selection"];
+ put?: never;
+ /** 选定探索 */
+ post: operations["select_exploration"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/plans/schema": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取规划结构入口 */
+ get: operations["read_plan_schema"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/plans": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 列出规划入口 */
+ get: operations["list_plans"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/plans/candidates": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 列出规划候选 */
+ get: operations["list_plan_candidates"];
+ put?: never;
+ /** 创建规划候选 */
+ post: operations["create_author_plan_candidate"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/plan-candidates/{candidate_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取规划候选 */
+ get: operations["read_plan_candidate"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/plan-candidates/{candidate_id}/versions": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 编辑规划候选 */
+ post: operations["edit_plan_candidate"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/plans/{plan_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取正式规划 */
+ get: operations["read_plan"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/plan-candidates/{candidate_id}/reviews": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 打开规划审阅 */
+ post: operations["open_plan_review"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/plan-candidates/{candidate_id}/decisions": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 决定规划候选 */
+ post: operations["decide_plan_candidate"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/chapters/{chapter_id}/fine-outline": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取可用细纲入口 */
+ get: operations["read_usable_fine_outline"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/plans/form": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取规划表单 */
+ get: operations["read_plan_form"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/chapters/{chapter_id}/documents/history": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取历史 */
+ get: operations["list_document_history"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/chapters/{chapter_id}/documents": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取正文 */
+ get: operations["read_document"];
+ put?: never;
+ /** 保存正文 */
+ post: operations["save_document"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/chapters/{chapter_id}/documents/restore": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 回退正文 */
+ post: operations["restore_document"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/chapters/{chapter_id}/writing-candidates": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 列出候选 */
+ get: operations["list_writing_candidates"];
+ put?: never;
+ /** 创建人工候选 */
+ post: operations["create_author_candidate"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/writing-candidates/{candidate_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取候选 */
+ get: operations["read_writing_candidate"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/writing-candidates/{candidate_id}/edits": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 修改候选 */
+ post: operations["edit_writing_candidate"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/author-reviews": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 打开审阅 */
+ post: operations["open_writing_review"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/writing-candidates/{candidate_id}/decisions": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 决定候选 */
+ post: operations["decide_writing_candidate"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/facts/schema": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取事实结构 */
+ get: operations["read_fact_schema"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/facts": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 查询事实 */
+ get: operations["query_world_state"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/works/{work_id}/fact-proposals": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 提出事实 */
+ post: operations["propose_world_fact"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/fact-proposals/{proposal_id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取提案 */
+ get: operations["read_fact_proposal"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/fact-proposals/{proposal_id}/reviews": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 打开事实审阅 */
+ post: operations["open_fact_review"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/fact-proposals/{proposal_id}/decisions": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /** 决定事实 */
+ post: operations["decide_world_fact"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/events": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取事件 */
+ get: operations["read_task_events"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/tasks/{task_id}/events/stream": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 订阅事件 */
+ get: operations["stream_task_events"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/health": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 存活 */
+ get: operations["read_health"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/ready": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 就绪 */
+ get: operations["read_readiness"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/v1/session": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** 读取 */
+ get: operations["read_session"];
+ put?: never;
+ /** 登录 */
+ post: operations["open_session"];
+ /** 退出 */
+ delete: operations["close_session"];
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+}
+export type webhooks = Record;
+export interface components {
+ schemas: {
+ /** HTTPValidationError */
+ HTTPValidationError: {
+ /** Detail */
+ detail?: components["schemas"]["ValidationError"][];
+ };
+ /** ValidationError */
+ ValidationError: {
+ /** Location */
+ loc: (string | number)[];
+ /** Message */
+ msg: string;
+ /** Error Type */
+ type: string;
+ /** Input */
+ input?: unknown;
+ /** Context */
+ ctx?: Record;
+ };
+ /** 段落 */
+ __: {
+ /** Paragraph Id */
+ paragraph_id: string;
+ /** Content */
+ content: (components["schemas"]["muse__________________8"] | components["schemas"]["muse__________________9"])[];
+ };
+ /** 值规则 */
+ "___-Input": {
+ /** 类型 */
+ "\u7C7B\u578B": string;
+ /**
+ * 枚举
+ * @default []
+ */
+ "\u679A\u4E3E": (string | number | boolean)[];
+ "\u5143\u7D20"?: components["schemas"]["___-Input"] | null;
+ /**
+ * 字段
+ * @default []
+ */
+ "\u5B57\u6BB5": components["schemas"]["muse_________________2"][];
+ /**
+ * 分支
+ * @default []
+ */
+ "\u5206\u652F": components["schemas"]["___-Input"][];
+ /** 最小值 */
+ "\u6700\u5C0F\u503C"?: number | null;
+ /** 最大值 */
+ "\u6700\u5927\u503C"?: number | null;
+ "\u5F15\u7528"?: components["schemas"]["muse_________________3"] | null;
+ /** 最少项 */
+ "\u6700\u5C11\u9879"?: number | null;
+ /** 最多项 */
+ "\u6700\u591A\u9879"?: number | null;
+ };
+ /** 事件页 */
+ "___-Output": {
+ /** Items */
+ items: components["schemas"]["muse______http______________2"][];
+ /** Reset Required */
+ reset_required: boolean;
+ /** First Sequence */
+ first_sequence: number;
+ /** Last Sequence */
+ last_sequence: number;
+ };
+ /** 任务控制请求 */
+ "muse_________________-Input": {
+ /** Command Id */
+ command_id: string;
+ /** Target Ref */
+ target_ref: string;
+ expected_state: components["schemas"]["muse__________________1"];
+ /**
+ * Action
+ * @enum {string}
+ */
+ action: "暂停" | "取消" | "恢复";
+ };
+ /** 字段用途 */
+ muse_________________1: {
+ /**
+ * Uivisible
+ * @default true
+ */
+ uiVisible: boolean;
+ /**
+ * Usereditable
+ * @default true
+ */
+ userEditable: boolean;
+ /**
+ * Usersearchable
+ * @default true
+ */
+ userSearchable: boolean;
+ /**
+ * Exportable
+ * @default true
+ */
+ exportable: boolean;
+ /**
+ * Aicontext
+ * @default false
+ */
+ aiContext: boolean | string[];
+ };
+ /** 字段定义 */
+ muse_________________2: {
+ /** Field Id */
+ field_id: string;
+ /** Key */
+ key: string;
+ /** 名称 */
+ "\u540D\u79F0": string;
+ "\u503C": components["schemas"]["___-Input"];
+ /**
+ * 说明
+ * @default
+ */
+ "\u8BF4\u660E": string;
+ /**
+ * 必填
+ * @default false
+ */
+ "\u5FC5\u586B": boolean;
+ /**
+ * 推荐
+ * @default false
+ */
+ "\u63A8\u8350": boolean;
+ /**
+ * 别名
+ * @default []
+ */
+ "\u522B\u540D": string[];
+ "\u7528\u9014"?: components["schemas"]["muse_________________1"];
+ /**
+ * 存储
+ * @default extension_json
+ */
+ "\u5B58\u50A8": string;
+ /** 绑定身份 */
+ "\u7ED1\u5B9A\u8EAB\u4EFD"?: string | null;
+ /**
+ * 已停用
+ * @default false
+ */
+ "\u5DF2\u505C\u7528": boolean;
+ };
+ /** 引用规则 */
+ muse_________________3: {
+ /** 目标类型 */
+ "\u76EE\u6807\u7C7B\u578B": string[];
+ /**
+ * 作品范围
+ * @default same_work
+ */
+ "\u4F5C\u54C1\u8303\u56F4": string;
+ /**
+ * 确认状态
+ * @default confirmed
+ */
+ "\u786E\u8BA4\u72B6\u6001": string;
+ /**
+ * 关系用途
+ * @default 依据
+ */
+ "\u5173\u7CFB\u7528\u9014": string;
+ };
+ /** 类型定义 */
+ muse_________________4: {
+ /** Type Id */
+ type_id: string;
+ /** 名称 */
+ "\u540D\u79F0": string;
+ /** 语义域 */
+ "\u8BED\u4E49\u57DF": string;
+ /** 实例粒度 */
+ "\u5B9E\u4F8B\u7C92\u5EA6": string | string[];
+ /** 实例族 */
+ "\u5B9E\u4F8B\u65CF": string[];
+ /**
+ * 说明
+ * @default
+ */
+ "\u8BF4\u660E": string;
+ };
+ /** 字段条件 */
+ muse_________________5: {
+ /** Key */
+ key: string;
+ /** 等于 */
+ "\u7B49\u4E8E": string;
+ /**
+ * 必填
+ * @default []
+ */
+ "\u5FC5\u586B": string[];
+ /**
+ * 禁止
+ * @default []
+ */
+ "\u7981\u6B62": string[];
+ };
+ /** 结构定义 */
+ muse_________________6: {
+ /** Type Id */
+ type_id: string;
+ /** Schema Id */
+ schema_id: string;
+ /** Schema Version */
+ schema_version: number;
+ /** 字段 */
+ "\u5B57\u6BB5": components["schemas"]["muse_________________2"][];
+ /** 父版本 */
+ "\u7236\u7248\u672C"?: number | null;
+ /**
+ * 条件
+ * @default []
+ */
+ "\u6761\u4EF6": components["schemas"]["muse_________________5"][];
+ };
+ /**
+ * 字段变更
+ * @description 业务content内的不重叠位置变更;数组结构变化用完整字段set。
+ */
+ muse_________________7: {
+ /** 路径 */
+ "\u8DEF\u5F84": (string | number)[];
+ /** 动作 */
+ "\u52A8\u4F5C": string;
+ /** 值 */
+ "\u503C"?: unknown;
+ };
+ /**
+ * 任务状态
+ * @enum {string}
+ */
+ muse__________________1: "queued" | "running" | "paused" | "reconciling" | "failed" | "cancelled" | "completed";
+ /** 正文草稿 */
+ muse__________________10: {
+ /** Paragraphs */
+ paragraphs: components["schemas"]["__"][];
+ /**
+ * Format Version
+ * @default 1
+ */
+ format_version: number;
+ };
+ /** 事实结构 */
+ muse__________________11: {
+ /** Schema Id */
+ schema_id: string;
+ /** Base Version */
+ base_version: number;
+ /** Effective Schema Hash */
+ effective_schema_hash: string;
+ /** Projection Version */
+ projection_version: string;
+ /** Extension Version */
+ extension_version?: number | null;
+ };
+ /** 引文锚点 */
+ muse__________________12: {
+ /** Paragraph Id */
+ paragraph_id: string;
+ /** Start */
+ start: number;
+ /** End */
+ end: number;
+ /** Quote */
+ quote: string;
+ };
+ /** 正文依据 */
+ muse__________________13: {
+ /** Chapter Id */
+ chapter_id: string;
+ /** Branch Id */
+ branch_id: string;
+ /** Revision */
+ revision: number;
+ /** Document Hash */
+ document_hash: string;
+ /** Evidence */
+ evidence: components["schemas"]["muse__________________12"][];
+ };
+ /** 知情记录 */
+ muse__________________14: {
+ /** Actor Id */
+ actor_id: string;
+ /** From Chapter Id */
+ from_chapter_id: string;
+ };
+ /** 叙事窗口 */
+ muse__________________15: {
+ /** From Chapter Id */
+ from_chapter_id: string;
+ /** To Chapter Id */
+ to_chapter_id: string;
+ };
+ /** 节点条目 */
+ muse__________________16: {
+ /** Node Id */
+ node_id: string;
+ /**
+ * Kind
+ * @enum {string}
+ */
+ kind: "volume" | "chapter" | "scene";
+ /** Title */
+ title: string;
+ /** Parent Id */
+ parent_id: string | null;
+ /** Position */
+ position: number;
+ };
+ /** 作品概要 */
+ muse__________________17: {
+ /** Work Id */
+ work_id: string;
+ /** Revision */
+ revision: number;
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ };
+ /** 章节条目 */
+ muse__________________18: {
+ /** Chapter Id */
+ chapter_id: string;
+ /** Title */
+ title: string;
+ /** Position */
+ position: number;
+ };
+ /** 目录结果 */
+ muse__________________19: {
+ /** Work Id */
+ work_id: string;
+ /** Revision */
+ revision: number;
+ /** Chapters */
+ chapters: components["schemas"]["muse__________________18"][];
+ };
+ /**
+ * 内容用途
+ * @description 内容消费用途。
+ * @enum {string}
+ */
+ muse__________________2: "planning" | "generation" | "detection" | "extraction";
+ /** 规划结果 */
+ muse__________________20: {
+ /** Plan Id */
+ plan_id: string;
+ /** Work Id */
+ work_id: string;
+ /** Chapter Id */
+ chapter_id: string | null;
+ /** Node Id */
+ node_id?: string | null;
+ /** Type Id */
+ type_id: string;
+ /** Revision */
+ revision: number;
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ /** Schema Binding */
+ schema_binding: {
+ [key: string]: unknown;
+ };
+ /** Source Dependencies */
+ source_dependencies: {
+ [key: string]: string;
+ }[];
+ };
+ /** 目录节点 */
+ muse__________________3: {
+ /** Node Id */
+ node_id: string;
+ /**
+ * Kind
+ * @enum {string}
+ */
+ kind: "volume" | "chapter" | "scene";
+ /** Title */
+ title: string;
+ /** Parent Id */
+ parent_id?: string | null;
+ };
+ /** 档案结构 */
+ muse__________________4: {
+ /** Schema Id */
+ schema_id: string;
+ /** Base Version */
+ base_version: number;
+ /** Effective Schema Hash */
+ effective_schema_hash: string;
+ /** Projection Version */
+ projection_version: string;
+ /** Extension Version */
+ extension_version?: number | null;
+ };
+ /** 探索选择 */
+ muse__________________5: {
+ /** Work Id */
+ work_id: string;
+ /** Draft Id */
+ draft_id: string;
+ /** Expected Hash */
+ expected_hash: string;
+ /** Expected Revision */
+ expected_revision: number;
+ };
+ /** 规划保存 */
+ muse__________________6: {
+ /** Work Id */
+ work_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ schema: components["schemas"]["muse__________________4"];
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ /** Chapter Id */
+ chapter_id?: string | null;
+ /** Selection Revision */
+ selection_revision?: number | null;
+ /**
+ * Changes
+ * @default []
+ */
+ changes: components["schemas"]["muse_________________7"][];
+ /** Node Id */
+ node_id?: string | null;
+ };
+ /** 规划定位 */
+ muse__________________7: {
+ /** Candidate Id */
+ candidate_id: string;
+ /** Candidate Revision */
+ candidate_revision: number;
+ /** Candidate Hash */
+ candidate_hash: string;
+ /** Plan Id */
+ plan_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ };
+ /** 文本节点 */
+ muse__________________8: {
+ /** Text */
+ text: string;
+ /**
+ * Marks
+ * @default []
+ */
+ marks: ("strong" | "emphasis")[];
+ /**
+ * Type
+ * @default text
+ * @constant
+ */
+ type: "text";
+ };
+ /** 换行节点 */
+ muse__________________9: {
+ /**
+ * Newline
+ * @default
+ * @enum {string}
+ */
+ newline: "\n" | "\r\n" | "\r";
+ /**
+ * Type
+ * @default line_break
+ * @constant
+ */
+ type: "line_break";
+ };
+ /** 任务控制回执 */
+ muse___________________2: {
+ /** Command Id */
+ command_id: string;
+ /** Task Id */
+ task_id: string;
+ state: components["schemas"]["muse__________________1"];
+ /** Last Sequence */
+ last_sequence: number;
+ };
+ /** 结构读取结果 */
+ muse___________________3: {
+ /** Schema Id */
+ schema_id: string;
+ /** Schema Version */
+ schema_version: number;
+ /** Type Id */
+ type_id: string;
+ /** Effective Schema Hash */
+ effective_schema_hash: string;
+ /** Contract */
+ contract: {
+ [key: string]: unknown;
+ };
+ };
+ /** 探索稿概要 */
+ muse___________________4: {
+ /** Draft Id */
+ draft_id: string;
+ /** Title */
+ title: string;
+ /** Revision */
+ revision: number;
+ /** Draft Hash */
+ draft_hash: string;
+ };
+ /** 探索稿结果 */
+ muse___________________5: {
+ /** Draft Id */
+ draft_id: string;
+ /** Title */
+ title: string;
+ /** Revision */
+ revision: number;
+ /** Draft Hash */
+ draft_hash: string;
+ /** Content */
+ content: string;
+ };
+ /** 已选探索稿 */
+ muse___________________6: {
+ /** Work Id */
+ work_id: string;
+ /** Revision */
+ revision: number;
+ snapshot: components["schemas"]["muse___________________5"];
+ /** Selected By */
+ selected_by: string;
+ /** Selected At */
+ selected_at: string;
+ };
+ /** 结构候选请求 */
+ muse____________________1: {
+ /**
+ * @description discriminator enum property added by openapi-typescript
+ * @enum {string}
+ */
+ action: "登记候选";
+ "\u7C7B\u578B": components["schemas"]["muse_________________4"];
+ "\u7ED3\u6784": components["schemas"]["muse_________________6"];
+ };
+ /** 探索检查请求 */
+ muse____________________10: {
+ /** Expected Hash */
+ expected_hash: string;
+ };
+ /** 探索选定请求 */
+ muse____________________11: {
+ /** Command Id */
+ command_id: string;
+ selection: components["schemas"]["muse__________________5"];
+ };
+ /** 规划候选请求 */
+ muse____________________12: {
+ /** Command Id */
+ command_id: string;
+ request: components["schemas"]["muse__________________6"];
+ };
+ /** 规划编辑请求 */
+ muse____________________13: {
+ /** Command Id */
+ command_id: string;
+ candidate: components["schemas"]["muse__________________7"];
+ /** Changes */
+ changes: components["schemas"]["muse_________________7"][];
+ };
+ /** 规划决定请求 */
+ muse____________________14: {
+ /** Command Id */
+ command_id: string;
+ candidate: components["schemas"]["muse__________________7"];
+ /**
+ * Action
+ * @enum {string}
+ */
+ action: "adopt" | "reject" | "defer";
+ /** Author Review Id */
+ author_review_id: string;
+ /** Review Hash */
+ review_hash: string;
+ /** Approved Changes */
+ approved_changes: string[];
+ };
+ /** 人工保存请求 */
+ muse____________________15: {
+ /** Command Id */
+ command_id: string;
+ /** Chapter Id */
+ chapter_id: string;
+ /**
+ * Branch Id
+ * @default main
+ */
+ branch_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ document: components["schemas"]["muse__________________10"];
+ };
+ /** 回退正文请求 */
+ muse____________________16: {
+ /** Command Id */
+ command_id: string;
+ /** Chapter Id */
+ chapter_id: string;
+ /**
+ * Branch Id
+ * @default main
+ */
+ branch_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ /** Restore From */
+ restore_from: number;
+ };
+ /** 候选修改请求 */
+ muse____________________17: {
+ /** Command Id */
+ command_id: string;
+ /** Candidate Id */
+ candidate_id: string;
+ /** Expected Candidate Revision */
+ expected_candidate_revision: number;
+ /** Expected Candidate Hash */
+ expected_candidate_hash: string;
+ document?: components["schemas"]["muse__________________10"] | null;
+ /** Selected Changes */
+ selected_changes?: string[];
+ };
+ /** 候选审阅请求 */
+ muse____________________18: {
+ /** Candidate Id */
+ candidate_id: string;
+ /** Candidate Revision */
+ candidate_revision: number;
+ /** Candidate Hash */
+ candidate_hash: string;
+ /** Target Ref */
+ target_ref: string;
+ /** Expected Revision */
+ expected_revision: number;
+ };
+ /** 候选决定请求 */
+ muse____________________19: {
+ /** Command Id */
+ command_id: string;
+ /** Candidate Id */
+ candidate_id: string;
+ /** Candidate Revision */
+ candidate_revision: number;
+ /** Candidate Hash */
+ candidate_hash: string;
+ /** Target Ref */
+ target_ref: string;
+ /** Expected Revision */
+ expected_revision: number;
+ /**
+ * Action
+ * @enum {string}
+ */
+ action: "adopt" | "reject" | "defer";
+ /** Author Review Id */
+ author_review_id: string;
+ /** Review Hash */
+ review_hash: string;
+ /** Approved Changes */
+ approved_changes?: string[];
+ };
+ /** 结构发布请求 */
+ muse____________________2: {
+ /**
+ * @description discriminator enum property added by openapi-typescript
+ * @enum {string}
+ */
+ action: "发布版本";
+ /** Schema Id */
+ schema_id: string;
+ /** Version */
+ version: number;
+ /** Content Hash */
+ content_hash: string;
+ };
+ /** 事实提案请求 */
+ muse____________________20: {
+ /** Work Id */
+ work_id: string;
+ /** Object Id */
+ object_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ schema: components["schemas"]["muse__________________11"];
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ /** Sources */
+ sources: components["schemas"]["muse__________________13"][];
+ /** Effective From */
+ effective_from: string | null;
+ /**
+ * Instance Family
+ * @default entity
+ * @enum {string}
+ */
+ instance_family: "entity" | "relation";
+ /**
+ * Independent
+ * @default false
+ */
+ independent: boolean;
+ /**
+ * Knowledge Mode
+ * @default fact
+ * @enum {string}
+ */
+ knowledge_mode: "fact" | "belief" | "plan" | "setting";
+ /**
+ * Known To
+ * @default []
+ */
+ known_to: components["schemas"]["muse__________________14"][];
+ /**
+ * Reader Visible
+ * @default true
+ */
+ reader_visible: boolean;
+ planned_window?: components["schemas"]["muse__________________15"] | null;
+ };
+ /** 事实创建请求 */
+ muse____________________21: {
+ /** Command Id */
+ command_id: string;
+ proposal: components["schemas"]["muse____________________20"];
+ };
+ /** 事实审阅请求 */
+ muse____________________22: {
+ /** Proposal Id */
+ proposal_id: string;
+ /** Proposal Revision */
+ proposal_revision: number;
+ /** Proposal Hash */
+ proposal_hash: string;
+ /** Target Ref */
+ target_ref: string;
+ /** Expected Revision */
+ expected_revision: number;
+ };
+ /** 事实决定请求 */
+ muse____________________23: {
+ /** Command Id */
+ command_id: string;
+ /** Proposal Id */
+ proposal_id: string;
+ /** Proposal Revision */
+ proposal_revision: number;
+ /** Proposal Hash */
+ proposal_hash: string;
+ /** Target Ref */
+ target_ref: string;
+ /** Expected Revision */
+ expected_revision: number;
+ /**
+ * Action
+ * @enum {string}
+ */
+ action: "adopt" | "reject" | "defer";
+ /** Author Review Id */
+ author_review_id: string;
+ /** Review Hash */
+ review_hash: string;
+ /** Approved Changes */
+ approved_changes?: string[];
+ };
+ /** 节点目录结果 */
+ muse____________________24: {
+ /** Work Id */
+ work_id: string;
+ /** Revision */
+ revision: number;
+ /** Nodes */
+ nodes: components["schemas"]["muse__________________16"][];
+ };
+ /** 正式回执结果 */
+ muse____________________25: {
+ /** Receipt Id */
+ receipt_id: string;
+ /** Command Id */
+ command_id: string;
+ /** Target Ref */
+ target_ref: string;
+ /** Action */
+ action: string;
+ /** Author Review Id */
+ author_review_id: string | null;
+ /** Basis */
+ basis: {
+ [key: string]: unknown;
+ };
+ /** Results */
+ results: {
+ [key: string]: unknown;
+ }[];
+ /** Impact Ids */
+ impact_ids: string[];
+ };
+ /** 候选进度结果 */
+ muse____________________26: {
+ /** Candidate Id */
+ candidate_id: string;
+ /** Revision */
+ revision: number;
+ /** Decision */
+ decision: string;
+ /** Stale */
+ stale: boolean;
+ };
+ /** 章节进度结果 */
+ muse____________________27: {
+ /** Chapter Id */
+ chapter_id: string;
+ /** Title */
+ title: string;
+ /** Position */
+ position: number;
+ /** Document Revision */
+ document_revision: number;
+ /** Document Hash */
+ document_hash: string;
+ /** Visible Characters */
+ visible_characters: number;
+ /** Candidates */
+ candidates: components["schemas"]["muse____________________26"][];
+ };
+ /** 任务进度结果 */
+ muse____________________28: {
+ /** Task Id */
+ task_id: string;
+ /** State */
+ state: string;
+ /** Event Cursor */
+ event_cursor: number;
+ };
+ /** 作品进度结果 */
+ muse____________________29: {
+ /** Work Id */
+ work_id: string;
+ /** Directory Revision */
+ directory_revision: number;
+ /** Total Chapters */
+ total_chapters: number;
+ /** Saved Chapters */
+ saved_chapters: number;
+ /** Undecided Candidates */
+ undecided_candidates: number;
+ /** Chapters */
+ chapters: components["schemas"]["muse____________________27"][];
+ /** Tasks */
+ tasks: components["schemas"]["muse____________________28"][];
+ };
+ /** 结构对照请求 */
+ muse____________________3: {
+ /**
+ * @description discriminator enum property added by openapi-typescript
+ * @enum {string}
+ */
+ action: "兼容检查";
+ /** Schema Id */
+ schema_id: string;
+ /** Base Version */
+ base_version: number;
+ "\u7ED3\u6784": components["schemas"]["muse_________________6"];
+ };
+ /** 作品表单结果 */
+ muse____________________30: {
+ /** Work Id */
+ work_id: string;
+ /** Revision */
+ revision: number;
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ schema_binding: components["schemas"]["muse__________________4"];
+ /** Visible Schema */
+ visible_schema: {
+ [key: string]: unknown;
+ };
+ /** Editable Schema */
+ editable_schema: {
+ [key: string]: unknown;
+ };
+ /** Visible Projection Version */
+ visible_projection_version: string;
+ };
+ /** 交付格式问题 */
+ muse____________________31: {
+ /** Code */
+ code: string;
+ /** Message */
+ message: string;
+ };
+ /** 探索检查结果 */
+ muse____________________32: {
+ /** Draft Id */
+ draft_id: string;
+ /** Draft Hash */
+ draft_hash: string;
+ /** Valid */
+ valid: boolean;
+ /** Problems */
+ problems: components["schemas"]["muse____________________31"][];
+ /** Setting Count */
+ setting_count: number;
+ /** Contract Version */
+ contract_version: string;
+ };
+ /** 探索选定状态 */
+ muse____________________33: {
+ /** Work Id */
+ work_id: string;
+ /** Revision */
+ revision: number;
+ selection: components["schemas"]["muse___________________6"] | null;
+ };
+ /** 规划候选结果 */
+ muse____________________34: {
+ /** Candidate Id */
+ candidate_id: string;
+ /** Plan Id */
+ plan_id: string;
+ /** Revision */
+ revision: number;
+ /** Candidate Hash */
+ candidate_hash: string;
+ /** Work Id */
+ work_id: string;
+ /** Chapter Id */
+ chapter_id: string | null;
+ /** Node Id */
+ node_id?: string | null;
+ /** Expected Revision */
+ expected_revision: number;
+ schema_binding: components["schemas"]["muse__________________4"];
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ /** Decision */
+ decision: string;
+ /** Visible Schema */
+ visible_schema: {
+ [key: string]: unknown;
+ };
+ /** Editable Schema */
+ editable_schema: {
+ [key: string]: unknown;
+ };
+ };
+ /** 规划审阅结果 */
+ muse____________________35: {
+ /** Review Id */
+ review_id: string;
+ /** Review Hash */
+ review_hash: string;
+ /** Snapshot */
+ snapshot: {
+ [key: string]: unknown;
+ };
+ candidate: components["schemas"]["muse____________________34"];
+ before: components["schemas"]["muse__________________20"] | null;
+ };
+ /** 规划表单结果 */
+ muse____________________36: {
+ /** Work Id */
+ work_id: string;
+ /** Chapter Id */
+ chapter_id: string | null;
+ /** Node Id */
+ node_id?: string | null;
+ /** Plan Id */
+ plan_id: string;
+ /** Revision */
+ revision: number;
+ schema_binding: components["schemas"]["muse__________________4"];
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ /** Visible Schema */
+ visible_schema: {
+ [key: string]: unknown;
+ };
+ /** Editable Schema */
+ editable_schema: {
+ [key: string]: unknown;
+ };
+ };
+ /** 正文历史结果 */
+ muse____________________37: {
+ /** Revision */
+ revision: number;
+ /** Document Hash */
+ document_hash: string;
+ /** Visible Text Hash */
+ visible_text_hash: string;
+ /** Restored From */
+ restored_from: number | null;
+ /**
+ * Created At
+ * Format: date-time
+ */
+ created_at: string;
+ };
+ /** 正文读取结果 */
+ muse____________________38: {
+ /** Work Id */
+ work_id: string;
+ /** Document Id */
+ document_id: string;
+ /** Chapter Id */
+ chapter_id: string;
+ /** Branch Id */
+ branch_id: string;
+ /** Revision */
+ revision: number;
+ /**
+ * State
+ * @enum {string}
+ */
+ state: "saved" | "unsaved";
+ document: components["schemas"]["muse__________________10"];
+ /** Visible Text */
+ visible_text: string;
+ /** Document Hash */
+ document_hash: string;
+ /** Visible Text Hash */
+ visible_text_hash: string;
+ };
+ /** 候选检查结果 */
+ muse____________________39: {
+ /** Format */
+ format: string;
+ /** Format Version */
+ format_version: number;
+ /** Quality */
+ quality: string;
+ };
+ /** 节点目录保存 */
+ muse____________________4: {
+ /** Work Id */
+ work_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ /** Nodes */
+ nodes: components["schemas"]["muse__________________3"][];
+ };
+ /** 候选差异结果 */
+ muse____________________40: {
+ /** Change Id */
+ change_id: string;
+ /** Start */
+ start: number;
+ /** End */
+ end: number;
+ /** Before */
+ before: components["schemas"]["__"][];
+ /** After */
+ after: components["schemas"]["__"][];
+ /** Before Text */
+ before_text: string;
+ /** After Text */
+ after_text: string;
+ };
+ /** 正文候选结果 */
+ muse____________________41: {
+ /** Candidate Id */
+ candidate_id: string;
+ /** Revision */
+ revision: number;
+ /** Candidate Hash */
+ candidate_hash: string;
+ /** Document Id */
+ document_id: string;
+ /** Chapter Id */
+ chapter_id: string;
+ /** Branch Id */
+ branch_id: string;
+ /** Origin */
+ origin: string;
+ /** Base Revision */
+ base_revision: number;
+ /** Current Document Revision */
+ current_document_revision: number;
+ document: components["schemas"]["muse__________________10"];
+ /** Visible Text */
+ visible_text: string;
+ checks: components["schemas"]["muse____________________39"];
+ /**
+ * Decision
+ * @enum {string}
+ */
+ decision: "undecided" | "adopt" | "reject" | "defer";
+ /** Stale */
+ stale: boolean;
+ /** Changes */
+ changes: components["schemas"]["muse____________________40"][];
+ };
+ /** 候选审阅结果 */
+ muse____________________42: {
+ /** Review Id */
+ review_id: string;
+ /** Review Hash */
+ review_hash: string;
+ /** Snapshot */
+ snapshot: {
+ [key: string]: unknown;
+ };
+ candidate: components["schemas"]["muse____________________41"];
+ before: components["schemas"]["muse____________________38"];
+ };
+ /** 事实来源版本 */
+ muse____________________43: {
+ /** Chapter Id */
+ chapter_id: string;
+ /** Branch Id */
+ branch_id: string;
+ /** Revision */
+ revision: number;
+ /** Document Hash */
+ document_hash: string;
+ };
+ /** 世界事实结果 */
+ muse____________________44: {
+ /** Object Id */
+ object_id: string;
+ /** Type Id */
+ type_id: string;
+ /** Revision */
+ revision: number;
+ /** System Revision */
+ system_revision: number;
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ /** Effective From */
+ effective_from: string | null;
+ /** Knowledge Mode */
+ knowledge_mode: string;
+ /** Independent */
+ independent: boolean;
+ /** Content Hash */
+ content_hash: string;
+ /** Projection Version */
+ projection_version: string;
+ /** Schema Binding */
+ schema_binding: {
+ [key: string]: unknown;
+ };
+ /** Sources */
+ sources: components["schemas"]["muse____________________43"][];
+ /** Planned Window */
+ planned_window?: {
+ [key: string]: string;
+ } | null;
+ };
+ /** 事实省略原因 */
+ muse____________________45: {
+ /** Object Id */
+ object_id: string;
+ /** Reason */
+ reason: string;
+ };
+ /** 世界查询结果 */
+ muse____________________46: {
+ /** Work Id */
+ work_id: string;
+ /** Directory Revision */
+ directory_revision: number;
+ /** System Revision */
+ system_revision: number;
+ /** Facts */
+ facts: components["schemas"]["muse____________________44"][];
+ /** Beliefs */
+ beliefs: components["schemas"]["muse____________________44"][];
+ /** Plans */
+ plans: components["schemas"]["muse____________________44"][];
+ /** Omitted */
+ omitted: components["schemas"]["muse____________________45"][];
+ };
+ /** 事实提案结果 */
+ muse____________________47: {
+ /** Proposal Id */
+ proposal_id: string;
+ /** Revision */
+ revision: number;
+ /** Proposal Hash */
+ proposal_hash: string;
+ /** Work Id */
+ work_id: string;
+ /** Object Id */
+ object_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ schema: components["schemas"]["muse__________________11"];
+ /** Content */
+ content: {
+ [key: string]: unknown;
+ };
+ /** Independent */
+ independent: boolean;
+ /** Sources */
+ sources: {
+ [key: string]: unknown;
+ }[];
+ /** Effective From */
+ effective_from: string | null;
+ /** Knowledge Mode */
+ knowledge_mode: string;
+ /** Known To */
+ known_to: components["schemas"]["muse__________________14"][];
+ /** Planned Window */
+ planned_window: {
+ [key: string]: string;
+ } | null;
+ /** Decision */
+ decision: string;
+ };
+ /** 事实审阅结果 */
+ muse____________________48: {
+ /** Review Id */
+ review_id: string;
+ /** Review Hash */
+ review_hash: string;
+ /** Snapshot */
+ snapshot: {
+ [key: string]: unknown;
+ };
+ proposal: components["schemas"]["muse____________________47"];
+ };
+ /** 节点目录请求 */
+ muse____________________5: {
+ /** Command Id */
+ command_id: string;
+ directory: components["schemas"]["muse____________________4"];
+ };
+ /** 档案保存请求 */
+ muse____________________6: {
+ /** Command Id */
+ command_id: string;
+ /** Work Id */
+ work_id: string;
+ /** Expected Revision */
+ expected_revision: number;
+ /** Content */
+ content?: {
+ [key: string]: unknown;
+ };
+ schema_binding: components["schemas"]["muse__________________4"];
+ /** Changes */
+ changes?: components["schemas"]["muse_________________7"][];
+ };
+ /** 新增章节请求 */
+ muse____________________7: {
+ /** Command Id */
+ command_id: string;
+ /** Work Id */
+ work_id: string;
+ /** Chapter Id */
+ chapter_id: string;
+ /** Title */
+ title: string;
+ /** Expected Directory Revision */
+ expected_directory_revision: number;
+ };
+ /** 重排章节请求 */
+ muse____________________8: {
+ /** Command Id */
+ command_id: string;
+ /** Work Id */
+ work_id: string;
+ /** Chapter Ids */
+ chapter_ids: string[];
+ /** Expected Directory Revision */
+ expected_directory_revision: number;
+ };
+ /** 探索保存请求 */
+ muse____________________9: {
+ /** Draft Id */
+ draft_id: string;
+ /** Title */
+ title: string;
+ /** Content */
+ content: string;
+ /** Expected Hash */
+ expected_hash: string | null;
+ };
+ /** 登录请求 */
+ "muse______http____________-Input": {
+ /**
+ * Password
+ * Format: password
+ */
+ password: string;
+ };
+ /** 事件响应 */
+ muse______http______________2: {
+ /** Event Id */
+ event_id: string;
+ /** Sequence */
+ sequence: number;
+ /** Event Type */
+ event_type: string;
+ /**
+ * Occurred At
+ * Format: date-time
+ */
+ occurred_at: string;
+ /** Payload */
+ payload: {
+ [key: string]: unknown;
+ };
+ };
+ /** 会话响应 */
+ muse______http______________3: {
+ /** Author Id */
+ author_id: string;
+ };
+ /** 步骤响应 */
+ muse______http__________________1: {
+ /** Step Id */
+ step_id: string;
+ /** State */
+ state: string;
+ };
+ /** 任务响应 */
+ muse______http__________________2: {
+ /** Task Id */
+ task_id: string;
+ state: components["schemas"]["muse__________________1"];
+ /** Work Id */
+ work_id: string | null;
+ /** Task Type */
+ task_type: string;
+ /** Last Sequence */
+ last_sequence: number;
+ /** Steps */
+ steps: components["schemas"]["muse______http__________________1"][];
+ };
+ /** 会话授权请求 */
+ muse______http____________________1: {
+ /** Command Id */
+ command_id: string;
+ /** Step Id */
+ step_id: string;
+ /** Stage */
+ stage: string;
+ /** Initial Request Hash */
+ initial_request_hash: string;
+ /** Max Model Calls */
+ max_model_calls: number;
+ /** Max Tool Calls */
+ max_tool_calls: number;
+ /**
+ * Valid Until
+ * Format: date-time
+ */
+ valid_until: string;
+ };
+ /** 原文授权请求 */
+ muse______http____________________2: {
+ /** Command Id */
+ command_id: string;
+ /** Source Version */
+ source_version: string;
+ /** Content Hashes */
+ content_hashes: string[];
+ content_purpose: components["schemas"]["muse__________________2"];
+ /**
+ * Retention Mode
+ * @enum {string}
+ */
+ retention_mode: "temporary" | "archive" | "persistent";
+ /**
+ * Valid Until
+ * Format: date-time
+ */
+ valid_until: string;
+ /** Call Id */
+ call_id?: string | null;
+ /** Call Request Hash */
+ call_request_hash?: string | null;
+ };
+ /** 原文租约请求 */
+ muse______http____________________3: {
+ /** Authorization Id */
+ authorization_id: string;
+ /** Command Id */
+ command_id: string;
+ /**
+ * Retain Until
+ * Format: date-time
+ */
+ retain_until: string;
+ /** Min Remaining */
+ min_remaining: number;
+ };
+ /** 原文归档请求 */
+ muse______http____________________4: {
+ /** Authorization Id */
+ authorization_id: string;
+ };
+ };
+ responses: never;
+ parameters: never;
+ requestBodies: never;
+ headers: never;
+ pathItems: never;
+}
+export type $defs = Record;
+export interface operations {
+ read_task: {
+ parameters: {
+ query?: {
+ work_id?: string | null;
+ };
+ header?: never;
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse______http__________________2"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_tasks: {
+ parameters: {
+ query?: {
+ work_id?: string | null;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse______http__________________2"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ control_task: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse_________________-Input"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse___________________2"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ approve_role_session: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse______http____________________1"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ revoke_raw_retention: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ authorization_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ approve_raw_retention: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse______http____________________2"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ create_raw_lease: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse______http____________________3"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ write_raw_bytes: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ lease_id: string;
+ content_hash: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/octet-stream": string;
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ archive_raw_bytes: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ lease_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse______http____________________4"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ cleanup_raw_lease: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ lease_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ recover_raw_leases: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ }[];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_evidence_receipt: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ task_id: string;
+ evidence_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ backfill_evidence_bytes: {
+ parameters: {
+ query: {
+ authorization_id: string;
+ };
+ header?: never;
+ path: {
+ task_id: string;
+ evidence_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/octet-stream": string;
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ schema_command: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________1"] | components["schemas"]["muse____________________2"] | components["schemas"]["muse____________________3"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_schema: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ schema_id: string;
+ version: number;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse___________________3"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_plan_nodes: {
+ parameters: {
+ query?: {
+ version?: number | null;
+ };
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________24"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ save_plan_nodes: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________5"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_work_progress: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________29"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_works: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse__________________17"][];
+ };
+ };
+ };
+ };
+ read_work_form: {
+ parameters: {
+ query?: {
+ schema_id?: string | null;
+ base_version?: number | null;
+ };
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________30"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ save_work_profile: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________6"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_work_chapters: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse__________________19"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ append_chapter: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________7"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ reorder_chapters: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________8"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_explorations: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse___________________4"][];
+ };
+ };
+ };
+ };
+ read_exploration: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ draft_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse___________________5"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ save_exploration: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ draft_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________9"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse___________________5"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ check_exploration_handoff: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ draft_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________10"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________32"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_exploration_selection: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________33"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ select_exploration: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________11"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_plan_schema: {
+ parameters: {
+ query: {
+ schema_id: string;
+ base_version: number;
+ };
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse__________________4"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_plans: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse__________________20"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_plan_candidates: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________34"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ create_author_plan_candidate: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________12"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_plan_candidate: {
+ parameters: {
+ query?: {
+ version?: number | null;
+ };
+ header?: never;
+ path: {
+ candidate_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________34"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ edit_plan_candidate: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ candidate_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________13"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_plan: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ plan_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse__________________20"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ open_plan_review: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ candidate_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse__________________7"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________35"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ decide_plan_candidate: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ candidate_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________14"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_usable_fine_outline: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ chapter_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse__________________20"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_plan_form: {
+ parameters: {
+ query: {
+ schema_id: string;
+ base_version: number;
+ chapter_id?: string | null;
+ node_id?: string | null;
+ };
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________36"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_document_history: {
+ parameters: {
+ query?: {
+ branch_id?: string;
+ };
+ header?: never;
+ path: {
+ chapter_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________37"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_document: {
+ parameters: {
+ query?: {
+ branch_id?: string;
+ revision?: number | null;
+ };
+ header?: never;
+ path: {
+ chapter_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________38"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ save_document: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ chapter_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________15"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ restore_document: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ chapter_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________16"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_writing_candidates: {
+ parameters: {
+ query?: {
+ branch_id?: string;
+ };
+ header?: never;
+ path: {
+ chapter_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________41"][];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ create_author_candidate: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ chapter_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________15"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_writing_candidate: {
+ parameters: {
+ query?: {
+ revision?: number | null;
+ };
+ header?: never;
+ path: {
+ candidate_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________41"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ edit_writing_candidate: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ candidate_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________17"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ open_writing_review: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________18"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________42"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ decide_writing_candidate: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ candidate_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________19"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_fact_schema: {
+ parameters: {
+ query: {
+ object_id: string;
+ schema_id: string;
+ base_version?: number;
+ instance_family?: "entity" | "relation";
+ extension_version?: number | null;
+ };
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse__________________11"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ query_world_state: {
+ parameters: {
+ query?: {
+ as_of?: string | null;
+ system_revision?: number | null;
+ view?: "author" | "reader" | "character";
+ character_id?: string | null;
+ };
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________46"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ propose_world_fact: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ work_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________21"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_fact_proposal: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ proposal_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________47"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ open_fact_review: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ proposal_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________22"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________48"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ decide_world_fact: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ proposal_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse____________________23"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse____________________25"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_task_events: {
+ parameters: {
+ query?: {
+ cursor?: number;
+ work_id?: string | null;
+ };
+ header?: never;
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["___-Output"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ stream_task_events: {
+ parameters: {
+ query?: {
+ cursor?: number;
+ work_id?: string | null;
+ };
+ header?: {
+ "last-event-id"?: number | null;
+ };
+ path: {
+ task_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ read_health: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: string;
+ };
+ };
+ };
+ };
+ };
+ read_readiness: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": unknown;
+ };
+ };
+ };
+ };
+ read_session: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse______http______________3"];
+ };
+ };
+ };
+ };
+ open_session: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["muse______http____________-Input"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["muse______http______________3"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ close_session: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: boolean;
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/web/src/界面/主题.css b/web/src/界面/主题.css
new file mode 100644
index 0000000..8eba819
--- /dev/null
+++ b/web/src/界面/主题.css
@@ -0,0 +1,563 @@
+:root {
+ font-family: "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
+ color: #30372e;
+ background: #f4f1e9;
+ font-synthesis: none;
+ --paper: #fffdf7;
+ --line: #dddccf;
+ --accent: #405e43;
+ --muted: #7d8073;
+}
+* {
+ box-sizing: border-box;
+}
+body {
+ margin: 0;
+ min-width: 320px;
+}
+button,
+input {
+ font: inherit;
+}
+button {
+ border: 1px solid var(--line);
+ padding: 12px 19px;
+ background: var(--paper);
+ color: inherit;
+ border-radius: 5px;
+ cursor: pointer;
+ transition:
+ transform 0.12s,
+ background 0.12s;
+}
+button:active {
+ transform: scale(0.98);
+}
+button:disabled {
+ opacity: 0.55;
+ cursor: wait;
+}
+button.primary {
+ background: var(--accent);
+ color: #fffdf5;
+ border-color: var(--accent);
+}
+button:hover {
+ filter: brightness(0.97);
+}
+input {
+ border: 1px solid #d7d7c9;
+ background: #fffcf6;
+ color: #30372e;
+ border-radius: 5px;
+ padding: 13px 15px;
+ min-width: 0;
+}
+input:focus,
+button:focus-visible,
+a:focus-visible {
+ outline: 2px solid #789472;
+ outline-offset: 3px;
+}
+label {
+ font-size: 13px;
+ font-weight: 600;
+}
+h1 {
+ font-size: 32px;
+ letter-spacing: -0.9px;
+ line-height: 1.3;
+ font-weight: 550;
+ margin: 10px 0 16px;
+}
+h2 {
+ font-size: 18px;
+ font-weight: 550;
+ margin: 0 0 16px;
+}
+p {
+ line-height: 1.9;
+}
+.muted {
+ color: var(--muted);
+ font-size: 13px;
+}
+.eyebrow {
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 2.5px;
+ color: #77806a;
+ margin: 0;
+}
+.brand-mark {
+ display: inline-grid;
+ place-items: center;
+ width: 52px;
+ height: 52px;
+ border: 1px solid #687361;
+ color: #53644c;
+ border-radius: 14px;
+ font-size: 30px;
+ font-family: inherit;
+ font-style: italic;
+}
+.brand-mark.small {
+ width: 38px;
+ height: 38px;
+ font-size: 23px;
+ border-radius: 10px;
+}
+.connect-page {
+ max-width: 1120px;
+ min-height: 100dvh;
+ margin: auto;
+ display: grid;
+ grid-template-columns: 1.25fr 1fr;
+ align-items: center;
+ gap: 90px;
+ padding: 70px;
+}
+.connect-intro h1 {
+ font-size: 58px;
+ line-height: 1.45;
+ letter-spacing: 1px;
+ margin: 32px 0 20px;
+}
+.connect-intro > .eyebrow {
+ margin-top: 28px;
+}
+.connect-intro > p:last-child {
+ color: #7c8272;
+ line-height: 2.2;
+ font-size: 15px;
+}
+.connect-form {
+ background: var(--paper);
+ border: 1px solid var(--line);
+ border-radius: 9px;
+ padding: 45px;
+ box-shadow: 0 18px 50px #41472d08;
+}
+.connect-form h2 {
+ font-size: 25px;
+ margin-top: 12px;
+}
+.connect-form label {
+ display: block;
+ margin: 25px 0 9px;
+}
+.connect-form input,
+.connect-form button {
+ width: 100%;
+}
+.connect-form button {
+ margin-top: 22px;
+}
+.loading-page {
+ min-height: 100dvh;
+ display: grid;
+ place-content: center;
+ gap: 20px;
+}
+.workbench {
+ display: grid;
+ grid-template-columns: 218px 1fr;
+ min-height: 100dvh;
+}
+.sidebar {
+ border-right: 1px solid var(--line);
+ padding: 31px 23px;
+ display: flex;
+ flex-direction: column;
+ background: #eeeee4;
+}
+.brand {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 60px;
+}
+.brand strong {
+ font-size: 23px;
+ font-weight: 550;
+ letter-spacing: 0.4px;
+ display: block;
+}
+.brand div > span {
+ display: block;
+ font-size: 10px;
+ color: var(--muted);
+ letter-spacing: 2px;
+ margin-top: 4px;
+}
+.nav-caption {
+ color: #929788;
+ font-size: 10px;
+ letter-spacing: 2px;
+ padding-left: 13px;
+ margin-bottom: 15px;
+}
+nav {
+ display: grid;
+ gap: 8px;
+}
+nav a {
+ padding: 13px;
+ color: #727969;
+ text-decoration: none;
+ border-radius: 5px;
+ font-size: 13px;
+ display: flex;
+ justify-content: space-between;
+}
+nav a.active {
+ background: #dde5d4;
+ color: #3f5a3b;
+}
+nav a > span {
+ font-size: 10px;
+ opacity: 0.55;
+ padding-top: 2px;
+}
+.sidebar-note {
+ margin-top: auto;
+ padding: 30px 3px 5px;
+ color: #68705e;
+ font-size: 11px;
+}
+.sidebar-note p {
+ font-size: 10px;
+ color: #9a9e90;
+ line-height: 1.8;
+}
+.status-dot {
+ display: inline-block;
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ background: #6f8c60;
+ margin-right: 7px;
+}
+.text-button {
+ border: 0;
+ padding: 8px 2px;
+ background: none;
+ text-align: left;
+ color: #959b8c;
+ font-size: 11px;
+}
+.workspace {
+ min-width: 0;
+}
+.topbar {
+ height: 72px;
+ border-bottom: 1px solid var(--line);
+ padding: 0 48px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 12px;
+ color: #757b6d;
+}
+.topbar > .muted {
+ font-size: 10px;
+ letter-spacing: 1px;
+}
+.content {
+ max-width: 1400px;
+ padding: 43px 48px 64px;
+ margin: 0 auto;
+}
+.page-heading {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 35px;
+}
+.page-heading .muted {
+ margin: 0;
+}
+.page-number {
+ font-size: 10px;
+ color: #a0a492;
+ letter-spacing: 1px;
+ padding-top: 7px;
+}
+.task-search {
+ padding: 24px 28px;
+ background: #eeede4;
+ border: 1px solid var(--line);
+ border-radius: 7px;
+}
+.task-search > div {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: 12px;
+ margin-top: 12px;
+}
+.task-search input {
+ font-size: 13px;
+}
+.empty-state {
+ text-align: center;
+ padding: 73px 20px 60px;
+ color: #7e8574;
+}
+.empty-symbol {
+ font-size: 35px;
+ color: #a5ae94;
+ display: block;
+ margin-bottom: 20px;
+}
+.empty-state h2 {
+ color: #59674e;
+ font-size: 17px;
+ font-weight: 500;
+ letter-spacing: 0.5px;
+}
+.empty-state p {
+ font-size: 12px;
+ line-height: 2.2;
+ color: #9a9f90;
+}
+.task-columns {
+ display: grid;
+ grid-template-columns: 1.35fr 1fr;
+ gap: 27px;
+ margin-top: 26px;
+}
+.paper-panel {
+ background: var(--paper);
+ border: 1px solid var(--line);
+ border-radius: 7px;
+ padding: 28px;
+}
+.panel-heading {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+}
+.panel-heading h2 {
+ margin: 0;
+ font-size: 16px;
+}
+.state-pill {
+ padding: 5px 9px;
+ border: 1px solid #d5dfc9;
+ background: #edf1e5;
+ color: #617551;
+ font-size: 10px;
+ border-radius: 3px;
+}
+.mono {
+ font-family: ui-monospace, monospace;
+ font-size: 10px;
+ overflow-wrap: anywhere;
+}
+.step-list,
+.event-list {
+ list-style: none;
+ padding: 0;
+ margin: 27px 0 0;
+}
+.step-list li {
+ display: flex;
+ gap: 20px;
+ padding: 19px 0;
+ border-top: 1px solid #e8e7de;
+}
+.step-number {
+ font-size: 11px;
+ color: #a3ab98;
+ padding-top: 3px;
+}
+.step-list strong {
+ font-size: 13px;
+ font-weight: 500;
+}
+.step-list li div > span {
+ display: block;
+ font-size: 11px;
+ color: #8a9380;
+ margin-top: 7px;
+}
+.event-panel {
+ padding: 28px 0;
+}
+.event-list li {
+ display: flex;
+ gap: 18px;
+ font-size: 12px;
+ line-height: 1.8;
+ padding: 11px 0;
+ border-bottom: 1px solid #e4e4d9;
+}
+.event-list time {
+ color: #9ca18f;
+ font-family: ui-monospace, monospace;
+ font-size: 10px;
+ padding-top: 2px;
+}
+.feedback {
+ padding: 15px 18px;
+ border: 1px solid #d7dfca;
+ background: #edf0e3;
+ color: #70815d;
+ font-size: 12px;
+ border-radius: 4px;
+ margin-top: 15px;
+}
+.feedback.error {
+ background: #f2eae1;
+ color: #8e5b3a;
+ border-color: #e3cfb9;
+}
+@media (max-width: 900px) {
+ .connect-page {
+ gap: 35px;
+ padding: 40px;
+ }
+ .connect-intro h1 {
+ font-size: 42px;
+ }
+ .workbench {
+ grid-template-columns: 175px 1fr;
+ }
+ .sidebar {
+ padding: 25px 15px;
+ }
+ .topbar {
+ padding: 0 26px;
+ }
+ .content {
+ padding: 32px 26px;
+ }
+ .task-columns {
+ grid-template-columns: 1fr;
+ }
+ .page-number {
+ display: none;
+ }
+}
+@media (max-width: 600px) {
+ .connect-page {
+ grid-template-columns: 1fr;
+ padding: 35px 22px;
+ gap: 26px;
+ }
+ .connect-intro h1 {
+ font-size: 34px;
+ margin: 20px 0 12px;
+ }
+ .connect-intro > p:last-child {
+ display: none;
+ }
+ .connect-form {
+ padding: 28px;
+ }
+ .workbench {
+ grid-template-columns: 1fr;
+ }
+ .sidebar {
+ border-right: 0;
+ border-bottom: 1px solid var(--line);
+ padding: 18px 22px;
+ display: block;
+ }
+ .brand {
+ margin: 0 0 17px;
+ }
+ .nav-caption,
+ .sidebar-note,
+ .text-button {
+ display: none;
+ }
+ nav {
+ display: flex;
+ }
+ nav a {
+ gap: 35px;
+ }
+ .topbar {
+ height: 55px;
+ padding: 0 22px;
+ }
+ .content {
+ padding: 29px 22px;
+ }
+ h1 {
+ font-size: 26px;
+ }
+ .task-search {
+ padding: 19px;
+ }
+ .task-search > div {
+ grid-template-columns: 1fr;
+ }
+ .empty-state {
+ padding-top: 45px;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ * {
+ transition: none !important;
+ scroll-behavior: auto !important;
+ }
+}
+
+.scope-filter {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ margin-bottom: 18px;
+}
+.scope-filter select {
+ font: inherit;
+ font-size: 12px;
+ color: #59674e;
+ padding: 9px 13px;
+ border: 1px solid var(--line);
+ border-radius: 4px;
+ background: #fffdf7;
+}
+.recent-tasks {
+ margin-top: 34px;
+}
+.recent-tasks ul {
+ list-style: none;
+ padding: 0;
+ margin-top: 20px;
+}
+.recent-tasks li {
+ border-top: 1px solid var(--line);
+}
+.recent-tasks li button {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ border: 0;
+ background: none;
+ padding: 21px 5px;
+ text-align: left;
+}
+.recent-tasks strong {
+ font-size: 14px;
+ font-weight: 500;
+}
+.recent-tasks small {
+ display: block;
+ color: #919985;
+ font-size: 11px;
+ margin-top: 8px;
+}
+.task-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 23px;
+}
+.task-actions button {
+ font-size: 12px;
+ padding: 9px 14px;
+}
diff --git a/web/src/界面/反馈状态.tsx b/web/src/界面/反馈状态.tsx
new file mode 100644
index 0000000..ed2cc9a
--- /dev/null
+++ b/web/src/界面/反馈状态.tsx
@@ -0,0 +1,16 @@
+export function Ui反馈状态({
+ message,
+ error = false,
+}: {
+ message: string;
+ error?: boolean;
+}) {
+ return (
+
+ {message}
+
+ );
+}
diff --git a/web/tests/交互/退出会话.test.tsx b/web/tests/交互/退出会话.test.tsx
new file mode 100644
index 0000000..6e332f3
--- /dev/null
+++ b/web/tests/交互/退出会话.test.tsx
@@ -0,0 +1,37 @@
+/** 组件级会话观察:传输替身不计真实登录旅程。 */
+import { afterEach, expect, it, vi } from "vitest";
+import { cleanup, fireEvent, render, screen } from "@testing-library/react";
+import { Ui应用 } from "../../src/应用/应用";
+import { Ui依赖装配 } from "../../src/应用/依赖装配";
+
+afterEach(() => {
+ cleanup();
+ vi.unstubAllGlobals();
+});
+
+it("NC-ui-session-close:退出成功卸载任务并回到连接表单", async () => {
+ vi.stubGlobal(
+ "fetch",
+ vi.fn(async (request: Request) => {
+ const path = new URL(request.url).pathname;
+ const data = path.endsWith("session")
+ ? request.method === "DELETE"
+ ? { closed: true }
+ : { author_id: "author-local" }
+ : [];
+ return new Response(JSON.stringify(data), {
+ headers: { "Content-Type": "application/json" },
+ });
+ }),
+ );
+ render(
+
+
+ ,
+ );
+ fireEvent.click(await screen.findByRole("button", { name: "断开会话" }));
+ expect(
+ await screen.findByRole("button", { name: "进入工作台" }),
+ ).toBeTruthy();
+ expect(screen.queryByRole("button", { name: "断开会话" })).toBeNull();
+});