diff --git a/Makefile b/Makefile
index cc5bacf..db2a601 100644
--- a/Makefile
+++ b/Makefile
@@ -88,7 +88,7 @@ install-webui:
STATIC_DIR = ksadk/server/static
OPEN_SOURCE_SMOKE_VENV ?= /tmp/ksadk-open-source-smoke
-OPEN_SOURCE_SMOKE_WHEEL := dist/ksadk-0.6.2-py3-none-any.whl
+OPEN_SOURCE_SMOKE_WHEEL ?= dist/ksadk-$(VERSION)-py3-none-any.whl
build-webui:
@echo "ℹ️ ksadk-python 不包含可编辑 Web UI 源码。"
@@ -224,6 +224,7 @@ PUBLIC_DOCS_URL ?= https://kingsoftcloud.github.io/ksadk-python/
PUBLIC_PYPI_PROJECT ?= ksadk
PUBLIC_ALIAS_PYPI_PROJECT ?= agentengine-sdk-python
PUBLIC_RELEASE_TAG ?= v$(V)
+PUBLIC_PUBLISH_PHASE ?= pre-publish
public-status:
@echo "==> public candidate"
@@ -296,7 +297,7 @@ public-preflight: public-audit public-test public-docs-build public-build-check
public-publish-check:
@echo "==> publication state check"
@if [ -f "scripts/check_publication_state.py" ]; then \
- uv run python scripts/check_publication_state.py --phase pre-publish; \
+ uv run python scripts/check_publication_state.py --phase "$(PUBLIC_PUBLISH_PHASE)" --version "$(VERSION)"; \
else \
echo "⚠️ scripts/check_publication_state.py 不存在,执行基础 HTTP 检查"; \
python3 -c 'import json, urllib.request; targets={"repo":"$(PUBLIC_REPO)","docs":"$(PUBLIC_DOCS_URL)","pypi":"https://pypi.org/pypi/$(PUBLIC_PYPI_PROJECT)/json","alias_pypi":"https://pypi.org/pypi/$(PUBLIC_ALIAS_PYPI_PROJECT)/json"}; [print("%s: HTTP %s%s" % (name, resp.status, ("\n version=%s" % json.load(resp)["info"].get("version")) if name.endswith("pypi") else "")) for name, url in targets.items() for resp in [urllib.request.urlopen(url, timeout=20)]]'; \
@@ -304,7 +305,7 @@ public-publish-check:
public-release-tag:
ifndef V
- $(error ❌ 请指定版本号,例如: make public-release-tag V=0.6.2)
+ $(error ❌ 请指定版本号,例如: make public-release-tag V=$(VERSION))
endif
@branch=$$(git branch --show-current); \
if [ "$$branch" != "$(PUBLIC_BRANCH)" ]; then \
diff --git a/README.zh-CN.md b/README.zh-CN.md
index dbc6809..1ea8b03 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -10,7 +10,7 @@
本地开发、Serverless 运行时、Google ADK、LangChain/LangGraph、
DeepAgents、Hermes、OpenClaw、MCP 和 Skill Runtime 等场景。
-当前版本:`0.6.2`。
+当前版本:`0.6.3`。
## 安装
@@ -64,6 +64,14 @@ agentengine launch . --target serverless
- AgentEngine 内置工具:skill 发现/加载、workspace 文件操作、component status、sandbox status 和 sandbox direct code/command execution
- Sandbox Runtime:通用沙箱抽象与 E2B 兼容后端
+## 0.6.3 重点
+
+- Hosted UI 与最新 gateway / server 对齐 `/hosted-ui/chat/`、share link、SSE 订阅和 native terminal 代理契约;`agentengine dashboard open` 继续优先打开托管入口,本地 `agentengine web` 保持调试用途。
+- LangGraph runner 在工具调用后即使没有文本流式 chunk,也会输出最终 answer,避免本地 Web UI 出现空 assistant message。
+- Skill Service 支持 `KSADK_SKILL_SERVICE_REGION=pre-online` 的 KOP 路由,自动设置预发所需 header。
+- OpenClaw / Hermes 更新已有实例时默认保留服务端已有 env、storage、network、memory 配置,只在显式传入对应 CLI 参数时覆盖。
+- `ksadk.toolsets`、Tool Gateway、Skill Runtime 与 Skill Service 相关文件纳入发布包,主推 LangGraph demo 可以在干净安装后直接绑定 AgentEngine 内置工具。
+
## 0.6.2 重点
- Skill Runtime 支持 Skill Space 远端发现、按需下载、`sha256` 校验、安全解压、`SKILL.md` instruction 加载,以及 `local_process` / E2B sandbox backend workflow 执行。
@@ -90,6 +98,7 @@ agentengine launch . --target serverless
- 文档:
- 仓库:
+- wiki:
- 示例仓库:
- Web UI 仓库:
- PyPI:
diff --git a/scripts/check_publication_state.py b/scripts/check_publication_state.py
new file mode 100644
index 0000000..48c34ad
--- /dev/null
+++ b/scripts/check_publication_state.py
@@ -0,0 +1,98 @@
+"""检查公开发布状态。
+
+发布前使用 `--phase pre-publish`,确保 GitHub Pages 可访问且 PyPI 上还没有
+当前版本;发布后使用 `--phase post-publish`,确保 PyPI 已能查询到当前版本。
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import sys
+import urllib.error
+import urllib.request
+from pathlib import Path
+
+
+ROOT = Path(__file__).resolve().parents[1]
+PYPROJECT = ROOT / "pyproject.toml"
+
+
+def _current_version() -> str:
+ for line in PYPROJECT.read_text(encoding="utf-8").splitlines():
+ if line.startswith("version = "):
+ return line.split("=", 1)[1].strip().strip('"')
+ raise RuntimeError("pyproject.toml 中未找到 version")
+
+
+def _open(url: str) -> tuple[int, bytes]:
+ request = urllib.request.Request(url, headers={"User-Agent": "ksadk-publication-check"})
+ with urllib.request.urlopen(request, timeout=20) as response:
+ return response.status, response.read()
+
+
+def _expect_http_ok(name: str, url: str) -> None:
+ status, _ = _open(url)
+ if status != 200:
+ raise RuntimeError(f"{name}: 期望 HTTP 200,实际 {status}: {url}")
+ print(f"{name}: HTTP {status}")
+
+
+def _pypi_project_version(project: str) -> str | None:
+ url = f"https://pypi.org/pypi/{project}/json"
+ try:
+ status, body = _open(url)
+ except urllib.error.HTTPError as exc:
+ if exc.code == 404:
+ return None
+ raise
+ if status != 200:
+ raise RuntimeError(f"{project}: 期望 HTTP 200,实际 {status}: {url}")
+ data = json.loads(body)
+ return data.get("info", {}).get("version")
+
+
+def _pypi_version_exists(project: str, version: str) -> bool:
+ url = f"https://pypi.org/pypi/{project}/{version}/json"
+ try:
+ status, _ = _open(url)
+ except urllib.error.HTTPError as exc:
+ if exc.code == 404:
+ return False
+ raise
+ if status != 200:
+ raise RuntimeError(f"{project}=={version}: 期望 HTTP 200 或 404,实际 {status}")
+ return True
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--phase", choices=("pre-publish", "post-publish"), required=True)
+ parser.add_argument("--version", default=_current_version())
+ parser.add_argument("--project", default="ksadk")
+ parser.add_argument("--alias-project", default="agentengine-sdk-python")
+ parser.add_argument("--docs-url", default="https://kingsoftcloud.github.io/ksadk-python/")
+ args = parser.parse_args()
+
+ _expect_http_ok("docs", args.docs_url)
+
+ latest = _pypi_project_version(args.project)
+ print(f"pypi:{args.project}: latest={latest}")
+
+ exists = _pypi_version_exists(args.project, args.version)
+ print(f"pypi:{args.project}=={args.version}: exists={exists}")
+
+ alias_latest = _pypi_project_version(args.alias_project)
+ print(f"pypi:{args.alias_project}: latest={alias_latest}")
+
+ if args.phase == "pre-publish" and exists:
+ raise RuntimeError(f"发布前检查失败:PyPI 已存在 {args.project}=={args.version}")
+ if args.phase == "post-publish" and not exists:
+ raise RuntimeError(f"发布后检查失败:PyPI 尚未存在 {args.project}=={args.version}")
+
+ print(f"✅ publication {args.phase} check passed for {args.project}=={args.version}")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())