mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 13:55:50 +00:00
围绕 ultracode skill 审查 agent 系统一致性后:
- ultracode.ts: 用系统提示版完整 Workflow 编排手册替换中文精简版
- HIGH#1 isolation:'worktree': claudeCodeBackend.run() 用 createAgentWorktree +
runWithCwdOverride 包裹 runAgent + finally 清理实现真正的 cwd 隔离;slug 用
sha256(runId:agentId) 派生以匹配 cleanupStaleAgentWorktrees 清理正则
(修 runId 为 w+base36 非 UUID 导致的泄漏盲区);worktree.ts 注释同步修正
- HIGH#2 inline 持久化: 新增 persistInlineScript,WorkflowTool + service 两条
inline 路径对称持久化到 .claude/workflow-runs/<runId>/script.js,返回可复用
scriptPath(闭环 inline→编辑→scriptPath 重提迭代循环)
- HIGH#3 opt-in 分工: ultracode/WorkflowTool/effort 注明 session reminder 由
harness 注入,repo 内无 ultracode 信号,保持 feature('WORKFLOW_SCRIPTS') +
isEnabled 两层 gate,不自造注入
- 测试: 新增 persistInline.test.ts;扩展 claudeCodeBackend(isolation 4 用例)/
WorkflowTool(inline)/service(scriptPath)/ultracode(harness)
含配套 workflow engine/panel 完善与 run-state-persistence design doc。
Co-Authored-By: Claude <noreply@anthropic.com>
148 lines
4.9 KiB
TypeScript
148 lines
4.9 KiB
TypeScript
// Agent 后端适配器抽象。引擎通过 registry 取 adapter 再调 run,不关心具体实现
|
||
// (Anthropic SDK / 核心 runAgent / OpenAI / 本地模型 / mock 均为 adapter 的实现)。
|
||
import type {
|
||
AgentProgressUpdate,
|
||
AgentRunParams,
|
||
AgentRunResult,
|
||
} from './types.js'
|
||
import type { HostHandle } from './ports.js'
|
||
|
||
/** adapter 能力声明。引擎/脚本据此降级(如后端不支持 schema 则改文本 + 解析)。 */
|
||
export type AgentAdapterCapabilities = {
|
||
/** 支持 schema 结构化输出(agent(schema) 直接返回对象)。 */
|
||
structuredOutput: boolean
|
||
/** 支持工具调用(仅核心 agent 后端有)。 */
|
||
tools?: boolean
|
||
/** 支持流式(v1 引擎不消费,预留)。 */
|
||
stream?: boolean
|
||
}
|
||
|
||
/** adapter.run 的上下文。 */
|
||
export type AgentAdapterContext = {
|
||
/** 透传的不透明 host 句柄(核心 adapter 用;独立后端忽略)。 */
|
||
host: HostHandle
|
||
/** 取消信号(与 workflow signal 一致)。 */
|
||
signal: AbortSignal
|
||
/** 当前 workflow runId(日志/追踪用)。 */
|
||
runId: string
|
||
/**
|
||
* 运行中进度上报(后端循环累计 token/tool 时调用)。可选:独立后端可不实现;
|
||
* 引擎据此发 agent_progress 事件(闭包带 agentId/runId 关联),面板实时刷新。
|
||
*/
|
||
onProgress?: (update: AgentProgressUpdate) => void
|
||
}
|
||
|
||
/**
|
||
* Agent 后端适配器。引擎只依赖此接口;具体后端实现它并注册到 registry。
|
||
* initialize/dispose 为可选生命周期(连接池/资源管理),由调用方通过
|
||
* registry.initializeAll/disposeAll 触发。
|
||
*/
|
||
export interface AgentAdapter {
|
||
/** 唯一标识(registry 路由 / 日志)。 */
|
||
readonly id: string
|
||
/** 能力声明。 */
|
||
readonly capabilities: AgentAdapterCapabilities
|
||
/** 执行一次 agent 调用。 */
|
||
run(params: AgentRunParams, ctx: AgentAdapterContext): Promise<AgentRunResult>
|
||
/** 初始化(由 registry.initializeAll 触发)。 */
|
||
initialize?(): Promise<void>
|
||
/** 销毁(由 registry.disposeAll 触发)。 */
|
||
dispose?(): Promise<void>
|
||
}
|
||
|
||
/** 路由规则:决定哪些 params 走哪个 adapter。按添加顺序匹配,先命中先用。 */
|
||
export type AdapterRouteRule =
|
||
| { kind: 'agentType'; agentType: string; adapter: string }
|
||
| { kind: 'model'; pattern: string; adapter: string }
|
||
| {
|
||
kind: 'custom'
|
||
match: (params: AgentRunParams) => boolean
|
||
adapter: string
|
||
}
|
||
|
||
/** registry 找不到匹配 adapter 时抛出。 */
|
||
export class AdapterNotFoundError extends Error {
|
||
constructor(message: string) {
|
||
super(message)
|
||
this.name = 'AdapterNotFoundError'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 多后端 registry。register 注册 adapter,route/default 配路由,resolve 按
|
||
* 规则顺序匹配选 adapter。adapter 的 lifecycle(initialize/dispose)通过
|
||
* initializeAll/disposeAll 统一触发(由调用方在运行前后调)。
|
||
*/
|
||
export class AgentAdapterRegistry {
|
||
private readonly adapters = new Map<string, AgentAdapter>()
|
||
private readonly rules: AdapterRouteRule[] = []
|
||
private defaultId: string | null = null
|
||
|
||
/** 注册一个 adapter(id 重复则覆盖)。链式。 */
|
||
register(adapter: AgentAdapter): this {
|
||
this.adapters.set(adapter.id, adapter)
|
||
return this
|
||
}
|
||
|
||
/** 设默认 adapter(无规则命中时用)。链式。 */
|
||
default(adapterId: string): this {
|
||
this.defaultId = adapterId
|
||
return this
|
||
}
|
||
|
||
/** 加一条路由规则(按添加顺序匹配)。链式。 */
|
||
route(rule: AdapterRouteRule): this {
|
||
this.rules.push(rule)
|
||
return this
|
||
}
|
||
|
||
has(id: string): boolean {
|
||
return this.adapters.has(id)
|
||
}
|
||
|
||
get(id: string): AgentAdapter | undefined {
|
||
return this.adapters.get(id)
|
||
}
|
||
|
||
/** 按规则匹配;第一个命中返回;无命中走 default;都没有抛 AdapterNotFoundError。 */
|
||
resolve(params: AgentRunParams): AgentAdapter {
|
||
for (const rule of this.rules) {
|
||
if (matchRule(rule, params)) {
|
||
const hit = this.adapters.get(rule.adapter)
|
||
if (hit) return hit
|
||
}
|
||
}
|
||
if (this.defaultId) {
|
||
const fallback = this.adapters.get(this.defaultId)
|
||
if (fallback) return fallback
|
||
}
|
||
throw new AdapterNotFoundError(
|
||
`无 adapter 匹配(rules=${this.rules.length}, default=${this.defaultId ?? '无'})`,
|
||
)
|
||
}
|
||
|
||
/** 触发所有 adapter 的 initialize(跳过未实现的)。 */
|
||
async initializeAll(): Promise<void> {
|
||
for (const a of this.adapters.values()) {
|
||
await a.initialize?.()
|
||
}
|
||
}
|
||
|
||
/** 触发所有 adapter 的 dispose(跳过未实现的)。 */
|
||
async disposeAll(): Promise<void> {
|
||
for (const a of this.adapters.values()) {
|
||
await a.dispose?.()
|
||
}
|
||
}
|
||
}
|
||
|
||
function matchRule(rule: AdapterRouteRule, params: AgentRunParams): boolean {
|
||
if (rule.kind === 'agentType') return params.agentType === rule.agentType
|
||
if (rule.kind === 'model') {
|
||
return (
|
||
typeof params.model === 'string' && params.model.startsWith(rule.pattern)
|
||
)
|
||
}
|
||
return rule.match(params) // custom
|
||
}
|