mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 00:35:51 +00:00
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:
124
packages/workflow-engine/examples/registry-demo.ts
Normal file
124
packages/workflow-engine/examples/registry-demo.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* registry 多后端路由演示(mock adapter,无需 API key)。
|
||||
*
|
||||
* 两个 adapter:strong(被 researcher 路由命中)+ fast(默认)。
|
||||
* 脚本里 agent({agentType:'researcher'}) → strong,其余 → fast。
|
||||
* 证明 agent 后端可通过 AgentAdapterRegistry 插拔 + 路由,引擎不关心实现。
|
||||
*
|
||||
* 用法:bun run packages/workflow-engine/examples/registry-demo.ts
|
||||
*/
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
AgentAdapterRegistry,
|
||||
createFileJournalStore,
|
||||
createHostHandle,
|
||||
runWorkflow,
|
||||
type AgentAdapter,
|
||||
type AgentRunParams,
|
||||
type AgentRunResult,
|
||||
type WorkflowPorts,
|
||||
} from '@claude-code-best/workflow-engine'
|
||||
|
||||
const strongAdapter: AgentAdapter = {
|
||||
id: 'strong',
|
||||
capabilities: { structuredOutput: true, tools: true },
|
||||
async run(p: AgentRunParams): Promise<AgentRunResult> {
|
||||
return {
|
||||
kind: 'ok',
|
||||
output: `[strong] ← ${p.prompt}`,
|
||||
usage: { outputTokens: 1 },
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const fastAdapter: AgentAdapter = {
|
||||
id: 'fast',
|
||||
capabilities: { structuredOutput: false },
|
||||
async run(p: AgentRunParams): Promise<AgentRunResult> {
|
||||
return {
|
||||
kind: 'ok',
|
||||
output: `[fast] ← ${p.prompt}`,
|
||||
usage: { outputTokens: 1 },
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const registry = new AgentAdapterRegistry()
|
||||
.register(strongAdapter)
|
||||
.register(fastAdapter)
|
||||
.route({ kind: 'agentType', agentType: 'researcher', adapter: 'strong' })
|
||||
.default('fast')
|
||||
|
||||
const SCRIPT = `
|
||||
export const meta = { name: 'registry-demo', description: 'multi-adapter routing' }
|
||||
phase('Route')
|
||||
const research = await agent('深度调研任务', { agentType: 'researcher', label: 'research' })
|
||||
const quick = await agent('快速小任务', { label: 'quick' })
|
||||
return { research, quick }
|
||||
`
|
||||
|
||||
function makePorts(runsDir: string): WorkflowPorts {
|
||||
return {
|
||||
// registry 优先,agentRunner 仅作形状占位(不会被调到)
|
||||
agentRunner: { runAgentToResult: async () => ({ kind: 'dead' }) },
|
||||
agentAdapterRegistry: registry,
|
||||
progressEmitter: {
|
||||
emit: e => {
|
||||
if (e.type === 'phase_started') console.log(`\n━ phase: ${e.phase}`)
|
||||
else if (e.type === 'agent_done') {
|
||||
const out =
|
||||
e.result.kind === 'ok'
|
||||
? String(e.result.output)
|
||||
: `[${e.result.kind}]`
|
||||
console.log(` ✓ ${e.label} → ${out}`)
|
||||
}
|
||||
},
|
||||
},
|
||||
taskRegistrar: {
|
||||
register: () => ({
|
||||
runId: 'demo',
|
||||
signal: new AbortController().signal,
|
||||
}),
|
||||
complete() {},
|
||||
fail() {},
|
||||
kill() {},
|
||||
pendingAction: () => null,
|
||||
},
|
||||
journalStore: createFileJournalStore(runsDir),
|
||||
permissionGate: { isAborted: () => false },
|
||||
logger: { debug: () => {}, event: () => {} },
|
||||
hostFactory: () => ({
|
||||
handle: createHostHandle(null),
|
||||
cwd: process.cwd(),
|
||||
budgetTotal: null,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
await registry.initializeAll()
|
||||
try {
|
||||
const result = await runWorkflow({
|
||||
script: SCRIPT,
|
||||
runId: `demo-${Date.now()}`,
|
||||
ports: makePorts(join(tmpdir(), 'wf-registry-demo')),
|
||||
host: createHostHandle(null),
|
||||
signal: new AbortController().signal,
|
||||
cwd: process.cwd(),
|
||||
budgetTotal: null,
|
||||
})
|
||||
console.log(`\n■ ${result.status}`)
|
||||
if (result.status === 'completed') {
|
||||
const ret = result.returnValue as { research: string; quick: string }
|
||||
console.log(
|
||||
`research(agentType:researcher) → ${ret.research.startsWith('[strong]') ? 'strong adapter ✓' : '??'}`,
|
||||
)
|
||||
console.log(
|
||||
`quick(默认) → ${ret.quick.startsWith('[fast]') ? 'fast adapter ✓' : '??'}`,
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
await registry.disposeAll()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user