Skip to content

Commit 3aab8a0

Browse files
committed
fix(release): publish alias package through trusted workflow
1 parent b1656c9 commit 3aab8a0

8 files changed

Lines changed: 180 additions & 35 deletions

.github/workflows/publish-pypi.yml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ on:
1313
approved_source_commit:
1414
description: Reviewed source commit SHA recorded in docs/maintainer-approval-record.md
1515
required: false
16+
publish_target:
17+
description: Publish target
18+
required: false
19+
default: full
20+
type: choice
21+
options:
22+
- full
23+
- alias-only
1624

1725
permissions:
1826
contents: read
@@ -31,6 +39,7 @@ jobs:
3139
env:
3240
KSADK_WEB_VERSION: ${{ github.event.inputs.ksadk_web_version || '0.2.18' }}
3341
KSADK_APPROVED_SOURCE_COMMIT: ${{ github.event.inputs.approved_source_commit || vars.KSADK_APPROVED_SOURCE_COMMIT }}
42+
PUBLISH_TARGET: ${{ github.event.inputs.publish_target || 'full' }}
3443
permissions:
3544
contents: read
3645
id-token: write
@@ -58,20 +67,35 @@ jobs:
5867
run: make sync-ksadk-web-static
5968

6069
- name: Run public release preflight
70+
if: env.PUBLISH_TARGET == 'full'
6171
run: make public-preflight
6272

73+
- name: Run alias-only release checks
74+
if: env.PUBLISH_TARGET == 'alias-only'
75+
run: make public-audit public-test public-build-alias-check
76+
6377
- name: Verify release approval gate
6478
run: make public-publish-gate
6579

6680
- name: Publish to PyPI
81+
if: env.PUBLISH_TARGET == 'full'
82+
uses: pypa/gh-action-pypi-publish@release/v1
83+
84+
- name: Build alias distribution
85+
if: env.PUBLISH_TARGET == 'full'
86+
run: make public-build-alias-check
87+
88+
- name: Publish alias package to PyPI
6789
uses: pypa/gh-action-pypi-publish@release/v1
90+
with:
91+
packages-dir: dist-alias
6892

6993
# 发版时同步部署 Fumadocs 文档站到 GitHub Pages, 文档与 PyPI 版本一致。
7094
# 用 if: always() 让文档部署不被 publish 阻塞(workflow_dispatch 重新部署文档时,
7195
# publish 会因版本门禁失败(版本未变),但 deploy-pages 仍应运行)。
7296
deploy-pages:
7397
needs: publish
74-
if: always()
98+
if: always() && (github.event_name != 'workflow_dispatch' || github.event.inputs.publish_target != 'alias-only')
7599
runs-on: ubuntu-latest
76100
environment:
77101
name: github-pages

CHANGELOG.md

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,22 @@
99

1010
### 亮点
1111

12-
- **run 状态双维度字段**:新增 `run_mode`(background/foreground/unknown)和 `run_trigger`(new_run/checkpoint_resume/approval_resume/unknown)两个独立维度字段,区分"怎么跑"和"怎么开始",替代单字段 `run_kind` 的语义错误。后台长任务从 checkpoint 恢复时不再丢失"这是后台任务"的信息,前端可直接消费 `ActiveRunMode` / `ActiveRunTrigger` 判断长任务会话,无需从事件流推断。
13-
- **checkpoint 可恢复性聚合字段**`ListSessionCheckpoints` 响应新增 `ResumableTotal` / `HasResumableCheckpoint`,解决 `Total > 0` 不能代表"可恢复"的误判(终态/过期/memory_local checkpoint 会让 Total 非空但不可恢复)。恢复按钮可用性应看 `HasResumableCheckpoint`
14-
- **state_delta.active_run 对齐**:ksadk 与 agentengine-server 现都把 `run_mode` / `run_trigger` 写入 `state_delta.active_run`,Session 对象的 `ActiveRunMode` / `ActiveRunTrigger` 由 state 重建,刷新/分享链接/切 session 都能恢复一致状态。
15-
- **会话事件续订与历史分页收敛**:runtime `ListSessionEvents` / `SubscribeRunEvents` 支持 `AfterSeqId` 增量续订,session backends 支持 `BeforeSeqId` 向前翻页,控制台可从最新窗口进入历史并稳定重连,不再依赖全量扫事件。
16-
- **真实 token usage 契约补齐**:ADK、LangChain、LangGraph runner 在单轮内累计多次 LLM 调用 usage,同时保留 `last_usage`,让服务端可以同时得到会话累计 token 消耗和最后一轮上下文窗口占用,避免用累计值误当窗口占用。
17-
18-
### 新增
19-
20-
- 新增 `ksadk/conversations/run_kinds.py``run_mode` / `run_trigger` 枚举常量 + `validate_run_mode` / `validate_run_trigger` + `trigger_from_resume_input`(从 resume_input 推导 trigger)。
21-
- `append_run_status_event` 新增 `run_mode` / `run_trigger` 参数,写入事件 `metadata``state_delta.active_run`
22-
- `PreparedConversationTurn` 新增 `run_mode` / `run_trigger` 字段,`build_run_input` 按 checkpoint_resume / approval_resume / new_run 分支回填。
23-
- `invoke_conversation_once` / `_iter_conversation_turn_events` / `stream_responses_conversation_turn` / `stream_conversation_turn` 透传 `run_mode`;18+ 处 `append_run_status_event` 调用点按 endpoint 语义传值。
24-
- 各 endpoint 按产品语义标记 run_mode:`RunAgent Background:true``ResumeRun Stream:true``background`;普通 `RunAgent Stream:true``ResumeRun Stream:false``/v1/responses``/v1/chat/completions``/run_sse``foreground`
25-
- `_DetachedSSEStream` 构造与终态 fallback 写入 `run_mode` / `run_trigger`
26-
- 新增 `_latest_session_run_metadata` helper(不改原 `_latest_session_run_status`,保护现有契约),`_session_to_action_payload` 顶层新增 `ActiveRunMode` / `ActiveRunTrigger`
27-
- `_list_checkpoints_payload` 新增 `ResumableTotal` / `HasResumableCheckpoint` 聚合字段。规则:`IsResumable===true && ReplayAllowed!==false && IsTerminal!==true && CheckpointStatus not in {expired, disabled}`,不排除 `resumed`(已恢复过的仍计入,符合存档点可反复读)。
28-
- agentengine-server 侧新建 `app/services/run_kinds.py`(独立维护,不 import ksadk,用测试约束一致性),`_append_run_status``_serialize_session` 同步写入/读取新字段。
29-
- 新增 `ksadk/runners/usage_accumulator.py`,统一归一化并累加 OpenAI/ADK/LangChain/LangGraph usage 字段,覆盖 `input_tokens``output_tokens``total_tokens` 及 token details。
30-
- runtime `ListSessionEvents` 新增 `AfterSeqId` / `BeforeSeqId` 过滤能力;`SubscribeRunEvents` 支持 `AfterSeqId`,用于断线后只推送已读序号之后的新事件。
31-
- `SessionService.get_events()` 在 in-memory、local SQLite、Postgres 后端补齐 `after_seq_id` / `before_seq_id` 过滤,保持最新窗口、向后增量、向前翻页三类语义一致。
12+
- **控制台会话恢复更稳**:Session 直接返回活跃 run 的结构化状态,刷新、切会话和分享链接进入时不再依赖前端全量扫描事件流来猜测是否需要重连。
13+
- **checkpoint 恢复按钮更准**`ListSessionCheckpoints` 返回可恢复 checkpoint 聚合结果,避免把终态、过期或禁用 checkpoint 误判成可恢复。
14+
- **长会话历史加载更可靠**:事件列表补齐向后增量续订和向前翻页语义,in-memory、SQLite、Postgres 后端保持一致,支持从最新窗口进入再加载更早历史。
15+
- **token usage 不再少算**:ADK、LangChain、LangGraph runner 会聚合同一轮里的多次模型调用,同时保留最后一次模型调用的 usage,便于服务端同时计算累计消耗和上下文窗口占用。
16+
- **公开发布门禁收敛**:公开仓 CI、源码审计、产物审计、Trusted Publishing 和 GitHub Pages 部署边界继续收紧,发版依赖 GitHub 可信流水线。
3217

3318
### 变更
3419

35-
- `run_status` 事件 `metadata``state_delta.active_run` 扩展为含 `run_mode` / `run_trigger`;旧 session 缺字段降级 `unknown`,不破坏现有 `ActiveInvocationId` / `ActiveRunStatus` 契约。
36-
- approval 续跑的 `run_mode` 跟随原 run(不写死 foreground),需从原 run 上下文透传。
37-
- server 侧 `run_status` 事件 `content` 仍为 `{status, detail}``run_mode` / `run_trigger` 只写进 `state_delta`,避免破坏现有消费方。
38-
- runner 返回 `metadata.usage` 继续表示单次响应的累计真实消耗;新增 `metadata.last_usage` 表示最后一次模型调用的 usage,供上层计算 `ContextUsage` 等窗口占用指标。
39-
- LangChain / LangGraph 的 final chunk metadata 增加 `usage``last_usage`,保留原响应内容结构,避免只取最后一个 chunk 或最后一次 LLM 调用造成 token 少算。
20+
- Session 响应新增活跃 run 的模式和触发来源字段,用于替代从事件流猜测运行形态的前端逻辑。
21+
- `run_status` 事件继续保持原 `content` 结构,新元数据写入 `metadata` / `state_delta.active_run`,避免破坏旧消费方。
22+
- Runtime event paging 的语义明确为:`AfterSeqId` 用于向后增量,`BeforeSeqId` 用于加载更早历史。
4023

4124
### 修复
4225

43-
- 修复 ADK runner 单轮内多次 LLM 调用时只保留最后一次 usage,导致会话累计 token 消耗少算的问题。
44-
- 修复 BaseRunner 从响应列表尾部反向取 usage,遇到多段模型调用时无法聚合的问题。
45-
- 修复 runtime 事件分页只支持 offset/limit,前端重连和历史向上翻页需要额外扫全量事件的问题。
26+
- 修复多次 LLM 调用只统计最后一次 usage,导致会话累计 token 消耗少算的问题。
27+
- 修复 runtime 历史分页只靠 offset/limit,长会话首次打开、向上翻页和重连补事件容易错页的问题。
4628
- 修复 `SubscribeRunEvents` hosted/runtime 双链路续订语义不一致,断线重连可能重复消费旧事件的问题。
4729

4830
## [0.6.8] - 2026-07-03

Makefile

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# AgentEngine Makefile
22
# 用于同步 KsADK Web static 和管理项目
33

4-
.PHONY: help install clean clean-cache clean-dist clean-static clean-offline dev test publish publish-test public-status public-init-worktree public-worktree-status public-sync-check public-secret-audit public-audit public-version-gate docs-site-build docs-site-dev public-test public-build-check public-preflight public-publish-check public-release-approval-check public-publish-gate public-release-tag public-review public-sync-ksadk-web-static open-source-audit-dist openclaw-build openclaw-push openclaw-size hermes-build hermes-push hermes-size sync-ksadk-web-static sync-hosted-ui build-frontend build-webui sync-static webui build-wheel build-all clean-frontend
4+
.PHONY: help install clean clean-cache clean-dist clean-static clean-offline dev test publish publish-test public-status public-init-worktree public-worktree-status public-sync-check public-secret-audit public-audit public-version-gate docs-site-build docs-site-dev public-test public-build-check public-build-alias-check public-preflight public-publish-check public-release-approval-check public-publish-gate public-release-tag public-review public-sync-ksadk-web-static open-source-audit-dist open-source-audit-alias-dist openclaw-build openclaw-push openclaw-size hermes-build hermes-push hermes-size sync-ksadk-web-static sync-hosted-ui build-frontend build-webui sync-static webui build-wheel build-all clean-frontend
55

66
# 默认目标
77
help:
@@ -377,6 +377,13 @@ public-build-check: clean-dist sync-ksadk-web-static
377377
@uv run --extra dev python -m twine check dist/*
378378
@$(MAKE) open-source-audit-dist
379379

380+
public-build-alias-check: sync-ksadk-web-static
381+
@echo "==> build and twine check alias distribution"
382+
@rm -rf dist-alias
383+
@uv run python scripts/build_alias_distribution.py --alias-project "$(PUBLIC_ALIAS_PYPI_PROJECT)" --out-dir dist-alias
384+
@uv run --extra dev python -m twine check dist-alias/*
385+
@$(MAKE) open-source-audit-alias-dist
386+
380387
open-source-audit-dist:
381388
@echo "==> audit wheel and sdist file lists"
382389
@if ! ls dist/*.whl dist/*.tar.gz >/dev/null 2>&1; then \
@@ -386,6 +393,15 @@ open-source-audit-dist:
386393
@python3 -c 'import glob, zipfile; [print(name) for path in sorted(glob.glob("dist/*.whl")) for name in zipfile.ZipFile(path).namelist()]' | python3 scripts/open_source_audit.py --target wheel --file-list -
387394
@python3 -c 'import glob, tarfile; [print(name) for path in sorted(glob.glob("dist/*.tar.gz")) for name in tarfile.open(path).getnames()]' | python3 scripts/open_source_audit.py --target sdist --file-list -
388395

396+
open-source-audit-alias-dist:
397+
@echo "==> audit alias wheel and sdist file lists"
398+
@if ! ls dist-alias/*.whl dist-alias/*.tar.gz >/dev/null 2>&1; then \
399+
echo "❌ alias dist artifacts missing; run make public-build-alias-check first"; \
400+
exit 1; \
401+
fi
402+
@python3 -c 'import glob, zipfile; [print(name) for path in sorted(glob.glob("dist-alias/*.whl")) for name in zipfile.ZipFile(path).namelist()]' | python3 scripts/open_source_audit.py --target wheel --file-list -
403+
@python3 -c 'import glob, tarfile; [print(name) for path in sorted(glob.glob("dist-alias/*.tar.gz")) for name in tarfile.open(path).getnames()]' | python3 scripts/open_source_audit.py --target sdist --file-list -
404+
389405
public-version-gate:
390406
@echo "==> release version gate (prevent downgrade/re-publish)"
391407
uv run python scripts/check_release_version.py

docs/maintainer-approval-record.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ Record exactly one approved source publication strategy.
3030
The approved strategy must name the reviewed commit, tag, pull request, or
3131
export archive used for:
3232

33-
- `ksadk-python`: clean export candidate from reviewed internal commit `eb76b17d7d3c176f7cf6126ceb001ab00f5d651d`; local candidate directory `/tmp/ksadk-python-export-candidate-0.6.9`; verified on 2026-07-08 with public source audit, registry-bundled `make public-preflight` in the public candidate worktree, Fumadocs static build, wheel/sdist build, twine check, and source/dist package audits.
34-
- `ksadk-web`: npm package `@kingsoftcloud/ksadk-web@0.2.18` from commit `24551d0f290e5a4efc5b5d60d02fa298cccd2efa`; Python candidate commit `eb76b17d7d3c176f7cf6126ceb001ab00f5d651d`; published by the trusted GitHub npm workflow on 2026-07-08 and consumed from the npm registry during `make public-preflight`.
33+
- `ksadk-python`: clean export candidate from reviewed internal commit `0ac219f0aea2214647f9a373537d39e27297c0ec`; local candidate directory `/tmp/ksadk-python-export-candidate-0.6.9-alias`; verified on 2026-07-08 with public source audit, release workflow contract tests, alias wheel/sdist build, twine check, and source/dist package audits. This candidate preserves the already-published `ksadk==0.6.9` release and adds trusted GitHub workflow support for publishing the compatibility alias package `agentengine-sdk-python==0.6.9`.
34+
- `ksadk-web`: npm package `@kingsoftcloud/ksadk-web@0.2.18` from commit `24551d0f290e5a4efc5b5d60d02fa298cccd2efa`; Python candidate commit `0ac219f0aea2214647f9a373537d39e27297c0ec`; published by the trusted GitHub npm workflow on 2026-07-08 and consumed from the npm registry during alias distribution verification.
3535

3636
Both approved source references must include the current commit SHA at approval
3737
time. This prevents a stale approval record from passing after candidate
@@ -50,7 +50,7 @@ changes.
5050
Hermes/OpenClaw default images, long-task resume, and terminal reconnect are
5151
covered by the staging E2E evidence.
5252
- GitHub PR checks are green on the reviewed commit.
53-
- Release notes and `CHANGELOG.md` were reviewed.
53+
- Release notes and `CHANGELOG.md` were reviewed, including the concise 0.6.9 summary.
5454
- Public README and docs were reviewed for sensitive environment names,
5555
internal endpoints, tokens, customer data, and inaccurate competitor claims.
5656
- PyPI/TestPyPI credentials stay outside the repository.

export-manifest.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"generatedAt": "2026-07-08T04:10:29.360629+00:00",
2+
"generatedAt": "2026-07-08T04:45:14.107433+00:00",
33
"targetRepository": "https://github.com/kingsoftcloud/ksadk-python",
44
"documentation": "https://kingsoftcloud.github.io/ksadk-python/",
5-
"exportPathCount": 555,
5+
"exportPathCount": 556,
66
"excludedPathCount": 201,
77
"excludedPaths": [
88
"docs/Agent 开发者上下文接入指南.md",
@@ -251,6 +251,7 @@
251251
],
252252
"scripts": [
253253
"scripts/audit_release_artifacts.py",
254+
"scripts/build_alias_distribution.py",
254255
"scripts/check_approval_record.py",
255256
"scripts/check_publication_state.py",
256257
"scripts/check_release_version.py",
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env python3
2+
"""Build a PyPI alias distribution for the ksadk package.
3+
4+
The alias package keeps the Python import/package layout as ``ksadk`` and only
5+
changes the distribution metadata name. It is used for the compatibility PyPI
6+
project ``agentengine-sdk-python``.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import argparse
12+
import re
13+
import shutil
14+
import subprocess
15+
import tempfile
16+
from pathlib import Path
17+
18+
19+
ROOT = Path(__file__).resolve().parents[1]
20+
DEFAULT_ALIAS_PROJECT = "agentengine-sdk-python"
21+
DEFAULT_ALIAS_DESCRIPTION = "Kingsoft Cloud Agent Engine SDK alias package for ksadk"
22+
23+
IGNORED_DIRS = {
24+
".git",
25+
".mypy_cache",
26+
".pytest_cache",
27+
".ruff_cache",
28+
".venv",
29+
".worktrees",
30+
".zread",
31+
"__pycache__",
32+
"build",
33+
"dist",
34+
"dist-alias",
35+
"htmlcov",
36+
"ksadk.egg-info",
37+
"node_modules",
38+
"site",
39+
}
40+
IGNORED_SUFFIXES = (".pyc", ".pyo")
41+
42+
43+
def _ignore(_dir: str, names: list[str]) -> set[str]:
44+
ignored: set[str] = set()
45+
for name in names:
46+
if name in IGNORED_DIRS or name.endswith(IGNORED_SUFFIXES):
47+
ignored.add(name)
48+
return ignored
49+
50+
51+
def _rewrite_pyproject(pyproject: Path, *, alias_project: str, description: str) -> None:
52+
text = pyproject.read_text(encoding="utf-8")
53+
text, name_count = re.subn(
54+
r'(?m)^name = "ksadk"$',
55+
f'name = "{alias_project}"',
56+
text,
57+
count=1,
58+
)
59+
text, description_count = re.subn(
60+
r'(?m)^description = ".*"$',
61+
f'description = "{description}"',
62+
text,
63+
count=1,
64+
)
65+
if name_count != 1:
66+
raise RuntimeError("pyproject.toml 中未找到唯一的 ksadk project name")
67+
if description_count != 1:
68+
raise RuntimeError("pyproject.toml 中未找到唯一的 project description")
69+
pyproject.write_text(text, encoding="utf-8")
70+
71+
72+
def build_alias_distribution(
73+
*,
74+
alias_project: str,
75+
description: str,
76+
out_dir: Path,
77+
) -> None:
78+
out_dir = out_dir.resolve()
79+
if out_dir.exists():
80+
shutil.rmtree(out_dir)
81+
out_dir.mkdir(parents=True, exist_ok=True)
82+
83+
with tempfile.TemporaryDirectory(prefix="ksadk-alias-src-") as tmp:
84+
alias_root = Path(tmp) / "src"
85+
shutil.copytree(ROOT, alias_root, ignore=_ignore)
86+
_rewrite_pyproject(
87+
alias_root / "pyproject.toml",
88+
alias_project=alias_project,
89+
description=description,
90+
)
91+
subprocess.run(
92+
["uv", "build", "--out-dir", str(out_dir)],
93+
cwd=alias_root,
94+
check=True,
95+
)
96+
97+
98+
def main() -> int:
99+
parser = argparse.ArgumentParser()
100+
parser.add_argument("--alias-project", default=DEFAULT_ALIAS_PROJECT)
101+
parser.add_argument("--description", default=DEFAULT_ALIAS_DESCRIPTION)
102+
parser.add_argument("--out-dir", type=Path, default=ROOT / "dist-alias")
103+
args = parser.parse_args()
104+
105+
build_alias_distribution(
106+
alias_project=args.alias_project,
107+
description=args.description,
108+
out_dir=args.out_dir,
109+
)
110+
return 0
111+
112+
113+
if __name__ == "__main__":
114+
raise SystemExit(main())

scripts/prepare_ksadk_python_export.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282

8383
SCRIPT_EXPORT_FILES = {
8484
"scripts/audit_release_artifacts.py",
85+
"scripts/build_alias_distribution.py",
8586
"scripts/check_approval_record.py",
8687
"scripts/check_publication_state.py",
8788
"scripts/check_release_version.py",

0 commit comments

Comments
 (0)