feat(workflow): add workflow engine, /workflows panel, /ultracode skill

将 feat/sdk-backend 分支中 workflow 相关的 20 个 commit 压缩为单 commit:

- 工作流引擎核心:phase / agent / parallel / pipeline 编排原语(packages/workflow-engine/)
- /workflows 面板:三区焦点布局(顶部 run tabs + 左侧 phase 侧栏 + 右侧 agent 列表)
- /ultracode skill:多 agent workflow 编排入口
- 进度存储 / journal / notification 系统
- WorkflowService 生命周期管理 + SentryErrorBoundary
- 脚本沙箱:禁用 dynamic import()、JSON args 防御性归一化
- journal 与 named-workflow 路径统一在 projectRoot
- 错误处理:parallel/pipeline hooks 错误日志、failure routing、semaphore abort
- workflow 工具升级为 core 工具 + PascalCase 命名

Co-Authored-By: glm-5.1 <zai-org@claude-code-best.win>
This commit is contained in:
claude-code-best
2026-06-13 20:07:18 +08:00
parent 91cffe16e2
commit d236880bc3
106 changed files with 16127 additions and 834 deletions

View File

@@ -0,0 +1,158 @@
// 深度集成后端:从活会话解析 agent/model/tools委托核心 runAgent。
// 实现 AgentAdapter 接口,由 registryU5注册并路由。
import {
type AgentAdapter,
type AgentAdapterContext,
type AgentRunParams,
type AgentRunResult,
} from '@claude-code-best/workflow-engine'
import { assembleToolPool } from '../../tools.js'
import { finalizeAgentTool } from '@claude-code-best/builtin-tools/tools/AgentTool/agentToolUtils.js'
import { runAgent } from '@claude-code-best/builtin-tools/tools/AgentTool/runAgent.js'
import {
isBuiltInAgent,
type AgentDefinition,
type BuiltInAgentDefinition,
} from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'
import { createUserMessage, extractTextContent } from '../../utils/messages.js'
import { createAgentId } from '../../utils/uuid.js'
import { logForDebugging } from '../../utils/debug.js'
import { logEvent } from '../../services/analytics/index.js'
import type { ModelAlias } from '../../utils/model/aliases.js'
import type { Message } from '../../types/message.js'
import type { ToolUseContext } from '../../Tool.js'
import { readHostBundle } from '../hostHandle.js'
/** workflow 子 agent 的兜底定义agentType 未命中真实注册表时用)。 */
export const WORKFLOW_AGENT: BuiltInAgentDefinition = {
agentType: 'workflow-worker',
whenToUse: 'workflow 脚本内 agent() 钩子派发的子任务',
tools: ['*'],
source: 'built-in',
baseDir: 'built-in',
getSystemPrompt: () =>
'You are a workflow sub-agent. Complete the task concisely; your final text is the return value relayed to the workflow.',
}
/** agentType → 真实 agent 注册表activeAgents 命中即用,否则兜底)。已导出便于单测。 */
export function resolveAgentDefinition(
agentType: string | undefined,
toolUseContext: ToolUseContext,
): AgentDefinition {
if (!agentType) return WORKFLOW_AGENT
const found = toolUseContext.options.agentDefinitions.activeAgents.find(
a => a.agentType === agentType,
)
return found ?? WORKFLOW_AGENT
}
/** model 别名 → 当前 provider 实际 model id。v1 直传(保留映射扩展点)。已导出便于单测。 */
export function mapWorkflowModel(
model: string | undefined,
): string | undefined {
return model
}
/** 从 agent 最终消息中提取 StructuredOutput 产出的 JSON 对象;失败返回 null。已导出便于单测。 */
export function extractStructuredOutput(
content: Array<{ type: string; text?: string }>,
): unknown | null {
for (const block of content) {
if (block.type === 'text' && block.text) {
const trimmed = block.text.trim()
const start = trimmed.indexOf('{')
const end = trimmed.lastIndexOf('}')
if (start >= 0 && end > start) {
try {
return JSON.parse(trimmed.slice(start, end + 1))
} catch {
// 继续尝试下一个文本块
}
}
}
}
return null
}
/** 深度集成后端:从活会话解析 agent/model/tools委托核心 runAgent。 */
export const claudeCodeBackend: AgentAdapter = {
id: 'claude-code',
capabilities: { structuredOutput: true, tools: true },
async run(
params: AgentRunParams,
ctx: AgentAdapterContext,
): Promise<AgentRunResult> {
const { toolUseContext, canUseTool } = readHostBundle(ctx.host)
const appState = toolUseContext.getAppState()
const agentDef = resolveAgentDefinition(params.agentType, toolUseContext)
const model = mapWorkflowModel(params.model)
const agentId = createAgentId()
const workerPermissionContext = {
...appState.toolPermissionContext,
mode: agentDef.permissionMode ?? 'acceptEdits',
}
const workerTools = assembleToolPool(
workerPermissionContext,
appState.mcp.tools,
)
// schema → 通过 prompt 追加 JSON Schema 指令(非交互模式 StructuredOutput 已启用)
const promptText = params.schema
? `${params.prompt}\n\nYou MUST return your final answer by calling the StructuredOutput tool with a value matching this JSON Schema:\n${JSON.stringify(params.schema)}`
: params.prompt
const promptMessages = [createUserMessage({ content: promptText })]
const messages: Message[] = []
const startTime = Date.now()
try {
for await (const msg of runAgent({
agentDefinition: agentDef,
promptMessages,
toolUseContext,
canUseTool,
isAsync: true,
querySource: toolUseContext.options.querySource ?? 'workflow',
availableTools: workerTools,
override: { agentId },
// runAgent 的 model 是顶层 ModelAliasworkflow 的 model 是任意别名串,
// 类型上不兼容,运行时由 provider 层解析。双重断言透传(优于 as any/never
...(model ? { model: model as unknown as ModelAlias } : {}),
})) {
messages.push(msg as Message)
}
} catch (e) {
logForDebugging(
`workflow sub-agent error (${agentDef.agentType}): ${(e as Error).message}`,
)
logEvent('tengu_workflow_agent', { ok: 0 })
return { kind: 'dead' }
}
const finalized = finalizeAgentTool(messages, agentId, {
prompt: params.prompt,
resolvedAgentModel: toolUseContext.options.mainLoopModel,
isBuiltInAgent: isBuiltInAgent(agentDef),
startTime,
agentType: agentDef.agentType,
isAsync: true,
})
const outputTokens =
finalized.usage?.output_tokens ?? finalized.totalTokens ?? 0
logEvent('tengu_workflow_agent', { ok: 1, outputTokens })
if (params.schema) {
const structured = extractStructuredOutput(finalized.content)
if (structured === null) return { kind: 'dead' }
return {
kind: 'ok',
output: structured as object,
usage: { outputTokens },
}
}
const text = extractTextContent(finalized.content, '\n')
return { kind: 'ok', output: text, usage: { outputTokens } }
},
}