mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 20:03:13 +08:00
feat(workflows): dynamic workflow orchestration end to end
A workflow is a JS script the model writes in the moment and hands to the Workflow tool, which runs it in a locked-down `node:vm` and orchestrates subagents through `agent()`/`parallel()`/`pipeline()`/`phase()`. Saving one as a `/name` command is the secondary path; the inline script is the point. Runtime: cross-realm value marshalling so a script cannot reach the host `Function`, determinism guards on `Date.now()`/`Math.random()` (they would make a resume replay diverge), a FIFO concurrency gate, and a journal that lets an interrupted run resume from its longest unchanged prefix. Desktop: the run shows up as a `workflow` section in the existing activity panel — phases as headings, their agents beneath. A workflow agent is an ordinary subagent run by the same runner, so its row opens the existing subagent page rather than a parallel viewer of its own; that needed a `by-agent` lookup, because these agents have no parent `Agent` tool call to key off. Finished runs are rebuilt from the per-agent sidecars when a session is reopened, since the live progress stream does not outlive the process.
This commit is contained in:
@@ -1,34 +1,67 @@
|
||||
// @generated stub from scan-missing-imports
|
||||
// 该文件自动生成,对应 ant-internal 的 feature() gated 模块。
|
||||
// 所有外部 build 的代码路径在 DCE 后都不会真的执行这里的代码,这只是
|
||||
// bun build resolver 的占位符。
|
||||
const __target = function noop() {}
|
||||
const __handler: ProxyHandler<any> = {
|
||||
get(_t, prop) {
|
||||
if (prop === '__esModule') return true
|
||||
if (prop === 'default') return new Proxy(__target, __handler)
|
||||
if (prop === Symbol.toPrimitive) return () => undefined
|
||||
if (prop === Symbol.iterator) return function* () {}
|
||||
if (prop === Symbol.asyncIterator) return async function* () {}
|
||||
if (prop === 'then') return undefined
|
||||
return new Proxy(__target, __handler)
|
||||
},
|
||||
apply() {
|
||||
return new Proxy(__target, __handler)
|
||||
},
|
||||
construct() {
|
||||
return new Proxy(__target, __handler)
|
||||
},
|
||||
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
|
||||
import type { Command } from '../../types/command.js'
|
||||
import { loadWorkflows } from '../../utils/workflows/discovery.js'
|
||||
import { areWorkflowsEnabled } from '../../utils/workflows/enabled.js'
|
||||
import type { WorkflowDefinition } from '../../utils/workflows/types.js'
|
||||
import { WORKFLOW_TOOL_NAME } from './constants.js'
|
||||
|
||||
/**
|
||||
* Turn every discovered workflow into a `/<name>` command.
|
||||
*
|
||||
* The command is a prompt, not a direct tool call: whatever the user typed
|
||||
* after the name has to become the `args` value, and only the model can decide
|
||||
* whether "issues 1024, 1025" is a list of numbers or a sentence. The prompt
|
||||
* pins everything else so the model's only job is that conversion.
|
||||
*/
|
||||
export async function getWorkflowCommands(cwd?: string): Promise<Command[]> {
|
||||
if (!areWorkflowsEnabled()) return []
|
||||
const workflows = await loadWorkflows(cwd)
|
||||
return workflows.map(workflow => createWorkflowCommand(workflow))
|
||||
}
|
||||
|
||||
export function createWorkflowCommand(workflow: WorkflowDefinition): Command {
|
||||
return {
|
||||
type: 'prompt',
|
||||
name: workflow.name,
|
||||
description: workflow.description,
|
||||
progressMessage: `running the ${workflow.name} workflow`,
|
||||
contentLength: workflow.description.length + 200,
|
||||
argNames: ['args'],
|
||||
source: workflowCommandSource(workflow),
|
||||
isEnabled: () => areWorkflowsEnabled(),
|
||||
userFacingName: () => workflow.name,
|
||||
async getPromptForCommand(args: string): Promise<ContentBlockParam[]> {
|
||||
const argsLine =
|
||||
args.trim() === ''
|
||||
? 'Pass no `args`.'
|
||||
: `Convert the following invocation text into the \`args\` value and pass it: ${JSON.stringify(args.trim())}. ` +
|
||||
'If it is a list of items, pass a real JSON array, not a string.'
|
||||
return [
|
||||
{
|
||||
type: 'text',
|
||||
text:
|
||||
`Run the saved workflow "${workflow.name}" by calling ${WORKFLOW_TOOL_NAME} exactly once with ` +
|
||||
`{ name: "${workflow.name}" }. ${argsLine}\n` +
|
||||
'Do not write a new script and do not do the work yourself — the saved workflow already ' +
|
||||
'contains the orchestration. After the tool returns, end your turn; the result arrives as a ' +
|
||||
'task notification.',
|
||||
},
|
||||
]
|
||||
},
|
||||
} as Command
|
||||
}
|
||||
|
||||
function workflowCommandSource(
|
||||
workflow: WorkflowDefinition,
|
||||
): 'builtin' | 'userSettings' | 'projectSettings' | 'plugin' {
|
||||
switch (workflow.source) {
|
||||
case 'built-in':
|
||||
return 'builtin'
|
||||
case 'plugin':
|
||||
return 'plugin'
|
||||
case 'projectSettings':
|
||||
return 'projectSettings'
|
||||
default:
|
||||
return 'userSettings'
|
||||
}
|
||||
}
|
||||
const stub: any = new Proxy(__target, __handler)
|
||||
export default stub
|
||||
export const __stubMissing = true
|
||||
// 兼容常见的命名导出 —— 没列在这里的也会通过 default Proxy 兜底
|
||||
export const createCachedMCState = stub
|
||||
export const isCachedMicrocompactEnabled = stub
|
||||
export const isModelSupportedForCacheEditing = stub
|
||||
export const getCachedMCConfig = stub
|
||||
export const markToolsSentToAPI = stub
|
||||
export const resetCachedMCState = stub
|
||||
export const checkProtectedNamespace = stub
|
||||
export const getCoordinatorUserContext = stub
|
||||
|
||||
Reference in New Issue
Block a user