@kanyun-ai-infra/agenthub
v0.4.0
Published
Typed TypeScript client for the Pilot v2 Control Plane API
Readme
@kanyun-ai-infra/agenthub
用于 agenthub Control Plane API 的类型化 TypeScript 客户端。
它直接调用 agenthub Control Plane,适合后端服务、CI 和工具以编程方式驱动
project → agent → version → session 生命周期。
接入时会遇到两个入口:
| 入口 | URL | 用途 |
| --- | --- | --- |
| Control Plane API | https://agenthub-plane.zhenguanyu.com | SDK / 你的服务调用。作为 controlPlaneUrl 传入。 |
| 管理门户 | https://agenthub.zhenguanyu.com | 人工发布和管理 agent,并在这里签发 API token。 |
两者使用不同的 token audience:Portal 登录态 token 不能调用 Control Plane,反过来也一样。
请始终把管理门户签发的 platform-api token 和 agenthub-plane Control Plane URL 配套使用。
安装
pnpm add @kanyun-ai-infra/agenthub
# 或:npm i @kanyun-ai-infra/agenthub需要 Node 18+,因为 SDK 使用全局 fetch。
如果调用方是 Java、Python、Go 或 Shell,请优先看 raw HTTP 指南和 REST 参考:
docs/user-guide/agenthub-http-quickstart.md,
docs/user-guide/agenthub-rest-api.md。
快速开始
import { PilotPlatformClient } from "@kanyun-ai-infra/agenthub";
const agenthub = new PilotPlatformClient({
controlPlaneUrl: "https://agenthub-plane.zhenguanyu.com",
token: process.env.PILOT_TOKEN, // 管理门户签发的 platform-api token
});
// 1. Project:租户 / 根分组。默认 runtime template 让发布 Version 时不必每次手选模板。
const project = await agenthub.projects.create({
slug: "acme",
displayName: "Acme",
defaultRuntimeTemplateRefName: "pilot-v2-pod-agent",
});
// 2. Agent:Project 下一个可运行的 agent。
await agenthub.agents.create({
projectId: project.id,
slug: "support-bot",
displayName: "Support Bot",
kind: "managed_agent",
defaults: {
model: "claude-opus-4-7",
systemPrompt: "You are the Acme support agent.",
agentPackage: {
format: "claude_code",
source: {
kind: "oss",
url: "https://bucket.oss-cn-beijing.aliyuncs.com/pilot/agent-packages/support-bot.tar.gz",
sha256: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
sizeBytes: 4096,
},
entryCommand: "报告",
},
},
});
// 3. Version:Agent 的不可变发布版本。Runtime source 默认继承 Project default。
const version = await agenthub.agents.publishVersion({
projectId: project.id,
agentSlug: "support-bot",
version: "v1",
workerContract: { kind: "managed_agent" },
setDefault: true,
managedAgentProfileSnapshot: {
model: "claude-opus-4-7",
systemPrompt: "You are the Acme support agent.",
},
});
// 4. Session:基于默认 Version(或指定 Version)分配一次运行时。
const session = await agenthub.sessions.create({
projectId: project.id,
agentId: version.agentId, // 用默认 Version;指定版本传 versionId
});
const ready = await agenthub.sessions.wait(session.sessionId); // status === "ready" 时返回核心模型
Project → Agent → Version → Session- Project:租户 / 根分组,拥有 agents 和 runtime templates。
- Agent:一个逻辑可运行应用。
kind是"managed_agent"(平台基于model+systemPrompt运行 agent loop)或"self_managed"(你自己提供 worker)。 - Version:Agent 的不可变发布版本,绑定一个 runtime source。用
setDefault标记 project/agent 默认版本。 - Session:基于某个 Version 发起的一次 startup/runtime allocation。
Agents 和 runtime templates 都是 project-scoped。Version 必须在同一个 Project 内解析到 Agent 和 runtime source;唯一例外是下文的 platform-shared templates。
本文一律用主词汇 agent / version;它们是底层 wire 名
workload/revision的别名,SDK 两套都接受,细节见 术语别名。
术语别名(agent / version)
| 主词汇(推荐) | 等价的底层名 | 说明 |
| --- | --- | --- |
| agenthub.agents | agenthub.workloads | 同一个资源实例(agents === workloads),方法两边都能调。 |
| agents.publishVersion({ agentSlug }) | workloads.publishRevision({ workloadSlug }) | 同一个调用;wire body 完全一致。 |
| sessions.create({ agentId, versionId }) | sessions.create({ workloadId, revisionId }) | 入参别名,内部映射回规范名后再发出。 |
| 返回字段 result.agentId / result.versionId | result.workloadId / result.revisionId | 返回对象同时带两套字段(别名 = 规范值)。 |
规则:
- 互斥:一对里只能传一个,规范名和别名同传会抛
ALIAS_CONFLICT,避免笔误被静默忽略 (sessions.create的agentId/workloadId、versionId/revisionId,applyRevisionManifest的agentSlug/workloadSlug)。 - 入参别名不进 wire:发请求前映射回
workloadId/revisionId,body 永远是规范名。 - 返回别名是 SDK 派生的可枚举字段(
JSON.stringify/ 日志都带得上),不回传服务端。 - 底层名(
workloads/publishRevision/workloadId)一直保留、行为不变,挑读着顺的那套写即可。 - 异步事件 payload、日志 / Trace 里的关联 ID 仍是
workloadId/revisionId(属 control-plane 范畴,不在 SDK 改名范围内)。
API 参考
agenthub.projects
| 方法 | 说明 |
| --- | --- |
| create({ slug, displayName, description?, defaultRuntimeTemplateRefName? }) | 创建 Project。slug 重复时返回 409 PROJECT_SLUG_CONFLICT。 |
| list() | 列出 token 有效 scope 内的 Project。 |
| get(projectId) | 查询单个 Project。 |
| delete(projectId) | 软删(停用)Project:清空 owner / members、关闭集成。owner-only(需 project:settings)。系统项目不可删。返回 { action: "deactivated", projectId }。 |
agenthub.agents(别名 agenthub.workloads)
| 方法 | 说明 |
| --- | --- |
| create(req, { dryRun? }) | 创建 Agent。返回 { mode, status, agentId?/workloadId?, workload }。 |
| publishVersion(req, { dryRun? }) | 发布 Version(别名 publishRevision,用 workloadSlug)。返回 { mode, status, agentId/workloadId, versionId?/revisionId?, runtimeTemplateId, resolved }。 |
| applyManifest({ projectId, manifest, dryRun? }) | 高级 / GitOps:apply raw agent manifest。 |
| applyRevisionManifest({ projectId, agentSlug \| workloadSlug, manifest, dryRun? }) | 高级 / GitOps:apply raw version manifest。两个 slug 拼写都接受(互斥)。 |
create / publishVersion 的 req 结构和 managed-agent 全部配置项(model /
systemPrompt / enabledSkills / enabledMcpServers / agentPackage /
agentOptions 安全约束 / runtime / startup 等),见
specs/features/agenthub-sdk.md
的 "Managed-Agent Configuration"、"Workload API Contract"、"Revision Publish Contract" 章节。
agentOptions安全约束(高频踩坑):agentOptions只能收紧 / 限额,不能放宽。allowedTools与平台基线取交集(只会更窄)、disallowedTools叠加平台黑名单且 不能移除平台守卫工具;permissionMode/env/mcpServers/hooks/plugins/ 原始sdkOptions等可能越权或逃逸 sandbox 的字段一律不开放(见 issue #840)。 字段全表见上述 spec。
apply* 方法里的 manifest 可以是 manifest 对象、{ yaml } 或 { json }。
agenthub.sessions
| 方法 | 说明 |
| --- | --- |
| create({ projectId, agentId, versionId?, configValues?, metadata?, idempotencyKey?, resources?, ... }) | 启动 Session。省略 versionId 时使用 Agent 默认 Version。agentId/versionId 也可写成 workloadId/revisionId(互斥)。resources 注入 Model A 只读输入文件(见下)。返回 { sessionId, status }。 |
| get(sessionId) | 读取 Session 快照({ session: { id, status, ... } },字段为服务端原样返回)。 |
| wait(sessionId, { intervalMs?, timeoutMs?, signal? }) | 轮询到 status === "ready";遇到 failed 或超时会抛错。 |
| releaseSandbox(sessionId) | 释放 Session 当前 sandbox(保留 session,下一轮 turn 自动 revive 到新 sandbox)。需 runs:create。返回 { released, sandboxId, sandboxDestroyed, destroyError? };sandboxDestroyed:false 不代表失败(watcher 会兜底销毁)。 |
Session status 取值为 created、provisioning、starting、ready、failed。
agenthub.files
| 方法 | 说明 |
| --- | --- |
| upload({ projectId, file, filename?, contentType? }) | 上传单个文件,返回 { ossUrl, url, filename, size, mediaType, mimeType }。file 接受 Blob / File / Uint8Array / ArrayBuffer;非具名 File 时必须传 filename。 |
文件落在 pilot/attachments/<projectId>/,与 Session 资源 scope 校验同一 keyspace —— 为某 Project
上传的文件无法挂到另一个 Project 的 Session。url 是 ~1h 的 presigned GET(仅预览用);
要持久引用的是 ossUrl。
Model A 只读输入文件(session resources)
把文件作为 Session 级只读输入挂进 sandbox 的 /workspace/.session/inputs/(每次
provision / revival 重新物化),用 files.upload + sessions.create({ resources }):
const up = await agenthub.files.upload({
projectId,
file: new Blob([await fs.promises.readFile("spec.md")]),
filename: "spec.md",
});
// `up` 是 SessionFileResource 的超集,只多一个预览用的 `url`;解构掉它,
// 剩下的 `meta`(ossUrl/filename/size/mediaType/mimeType)正是一个资源条目。
const { url, ...meta } = up;
const { sessionId } = await agenthub.sessions.create({
projectId,
agentId,
resources: {
version: "v1",
files: [{ ...meta, mountPath: "docs/spec.md" }], // mountPath 可省略 → 用文件名
},
});
// agent 在 /workspace/.session/inputs/docs/spec.md 只读看到该文件约束:每 Session ≤ 20 个文件;mountPath 必须是 .session/inputs/ 下的相对子路径
(拒绝绝对路径与 ..);只读。
Client 配置
new PilotPlatformClient(config)
| 选项 | 必填 | 说明 |
| --- | --- | --- |
| controlPlaneUrl | 是¹ | Control Plane 源地址。相对路径会拼到它下面;跨 origin 路径会在发 token 前被拒绝。 |
| token | 是¹ | Platform-api token。每个请求都会作为 Authorization: Bearer <token> 发送。 |
| fetch | 否 | 自定义 fetch 实现,默认使用全局 fetch。 |
| signal | 否 | 应用于每个请求的默认 AbortSignal。 |
| defaultHeaders | 否 | 合并到每个请求的额外 header。 |
| userAgent | 否 | 覆盖默认 @kanyun-ai-infra/agenthub/<version> UA。 |
| pilotApiVersion | 否 | 固定 x-pilot-api-version header,默认使用 v2 wire version。 |
¹ controlPlaneUrl 会依次回退到 PILOT_CONTROL_PLANE_URL、
KANYUN_PILOT_CONTROL_PLANE_URL;token 会回退到 PILOT_TOKEN。任意一项缺失时,
SDK 会在发送请求前抛出类型化错误。
获取 token
登录 管理门户 https://agenthub.zhenguanyu.com 并签发 platform-api token。
该 token 的作用域是 Control Plane API(agenthub-plane.zhenguanyu.com);可以存为
PILOT_TOKEN,也可以显式传给 token。Token 是 Bearer 凭据,请不要提交到代码仓库;
泄露后需要在门户里轮换。
Runtime template
managed-agent Version 运行在一个 runtime template(已注册的 runtime source)上。 Control Plane 按以下顺序解析:
- version
runtime.profileRef(structured)/spec.runtime.profileRef - version
spec.runtimeTemplateRef(manifest) - agent defaults
spec.defaults.runtimeTemplateRef(manifest) - project
defaultRuntimeTemplateRefName
如果都解析不到,返回 PROJECT_DEFAULT_RUNTIME_MISSING。如果指定模板在当前 Project
或 platform-shared templates 中不存在,返回 RUNTIME_TEMPLATE_NOT_FOUND。
主路径建议在创建 Project 时设置默认模板,这样发布 Version 时不需要手选:
await agenthub.projects.create({
slug: "acme",
displayName: "Acme",
defaultRuntimeTemplateRefName: "pilot-v2-pod-agent",
});否则需要在每个 Version 上显式指定:runtime: { profileRef: { name: "..." } }。
Platform-shared templates。 Platform Default project 拥有的模板(例如
pilot-v2-pod-agent)是共享基础设施:当调用方 Project 内找不到同名模板时,解析会回退到
Platform Default,因此任意 Project 的 Version 都可以按名称引用它们。echo-bot
是内置 echo-only 模板,适合 smoke test;它不会运行 LLM。
模型
model 接受完整 id(claude-opus-4-7、claude-sonnet-4-6、claude-haiku-4-5),
也接受会映射到完整 id 的历史别名:
| Alias | 解析为 |
| --- | --- |
| opus | claude-opus-4-7 |
| sonnet | claude-sonnet-4-6 |
| haiku | claude-haiku-4-5 |
Dry runs
agents.create、publishVersion 和 apply* 方法都接受 { dryRun: true }。
dry run 会校验并解析请求,但不持久化;返回 mode: "dry_run" 和
would_create / would_update 状态;Version 已存在且内容一致时可能返回
existing。正式发布前可以用它确认 runtime template 能正确解析。
错误
所有 API 请求失败都会抛出类型化错误,错误对象带 status、code 和 requestId:
| Class | HTTP |
| --- | --- |
| PilotPlatformUnauthenticatedError | 401 |
| PilotPlatformForbiddenError | 403 |
| PilotPlatformNotFoundError | 404 |
| PilotPlatformServerError | 5xx |
| PilotPlatformApiError | 上述错误的基类 |
import { PilotPlatformNotFoundError } from "@kanyun-ai-infra/agenthub";
try {
await agenthub.sessions.get(sessionId);
} catch (err) {
if (err instanceof PilotPlatformNotFoundError) {
// err.status === 404, err.code === "SESSION_NOT_FOUND"
}
throw err;
}