@polka-codes/core 0.9.78 → 0.9.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1904,6 +1904,935 @@ var agentWorkflow = async (input, { step, tools, logger }) => {
1904
1904
  throw new Error("Maximum number of tool round trips reached.");
1905
1905
  };
1906
1906
 
1907
+ // src/workflow/dynamic.ts
1908
+ import { parse } from "yaml";
1909
+ import { z as z22 } from "zod";
1910
+
1911
+ // src/workflow/dynamic-types.ts
1912
+ import { z as z21 } from "zod";
1913
+ var WorkflowInputDefinitionSchema = z21.object({
1914
+ id: z21.string(),
1915
+ description: z21.string().nullish(),
1916
+ default: z21.any().nullish()
1917
+ });
1918
+ var WorkflowStepDefinitionSchema = z21.object({
1919
+ id: z21.string(),
1920
+ tools: z21.array(z21.string()).nullish(),
1921
+ task: z21.string(),
1922
+ output: z21.string().nullish(),
1923
+ expected_outcome: z21.string().nullish(),
1924
+ /**
1925
+ * Persisted JavaScript/TypeScript (JS-compatible) async function body.
1926
+ * The code is wrapped as: `async (ctx) => { <code> }`.
1927
+ */
1928
+ code: z21.string().nullish(),
1929
+ /**
1930
+ * Optional JSON schema or other metadata for future structured outputs.
1931
+ * Not interpreted by core today.
1932
+ */
1933
+ outputSchema: z21.any().nullish(),
1934
+ /**
1935
+ * Optional timeout in milliseconds. Step execution will be aborted if it exceeds this duration.
1936
+ */
1937
+ timeout: z21.number().positive().nullish()
1938
+ });
1939
+ var WorkflowDefinitionSchema = z21.object({
1940
+ task: z21.string(),
1941
+ inputs: z21.array(WorkflowInputDefinitionSchema).nullish(),
1942
+ steps: z21.array(WorkflowStepDefinitionSchema),
1943
+ output: z21.string().nullish()
1944
+ });
1945
+ var WorkflowFileSchema = z21.object({
1946
+ workflows: z21.record(z21.string(), WorkflowDefinitionSchema)
1947
+ });
1948
+
1949
+ // src/workflow/dynamic.ts
1950
+ function parseDynamicWorkflowDefinition(source) {
1951
+ try {
1952
+ const raw = parse(source);
1953
+ const validated = WorkflowFileSchema.safeParse(raw);
1954
+ if (!validated.success) {
1955
+ return { success: false, error: z22.prettifyError(validated.error) };
1956
+ }
1957
+ return { success: true, definition: validated.data };
1958
+ } catch (error) {
1959
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
1960
+ }
1961
+ }
1962
+ var AsyncFunction = Object.getPrototypeOf(async () => {
1963
+ }).constructor;
1964
+ function validateAndApplyDefaults(workflowId, workflow, input) {
1965
+ if (!workflow.inputs || workflow.inputs.length === 0) {
1966
+ return input;
1967
+ }
1968
+ const validatedInput = {};
1969
+ const errors = [];
1970
+ for (const inputDef of workflow.inputs) {
1971
+ const providedValue = input[inputDef.id];
1972
+ if (providedValue !== void 0 && providedValue !== null) {
1973
+ validatedInput[inputDef.id] = providedValue;
1974
+ } else if (inputDef.default !== void 0 && inputDef.default !== null) {
1975
+ validatedInput[inputDef.id] = inputDef.default;
1976
+ } else {
1977
+ errors.push(`Missing required input '${inputDef.id}'${inputDef.description ? `: ${inputDef.description}` : ""}`);
1978
+ }
1979
+ }
1980
+ if (errors.length > 0) {
1981
+ throw new Error(`Workflow '${workflowId}' input validation failed:
1982
+ ${errors.map((e) => ` - ${e}`).join("\n")}`);
1983
+ }
1984
+ return validatedInput;
1985
+ }
1986
+ function createRunWorkflowFn(args) {
1987
+ return async (subWorkflowId, subInput) => {
1988
+ const mergedInput = { ...args.input, ...args.state, ...subInput ?? {} };
1989
+ return await args.runInternal(subWorkflowId, mergedInput, args.context, args.state);
1990
+ };
1991
+ }
1992
+ function compileStep(stepDef, workflowId, compiledSteps) {
1993
+ const key = `${workflowId}.${stepDef.id}`;
1994
+ const existing = compiledSteps.get(key);
1995
+ if (existing) {
1996
+ return existing;
1997
+ }
1998
+ if (!stepDef.code) {
1999
+ throw new Error(`Step '${stepDef.id}' in workflow '${workflowId}' has no code`);
2000
+ }
2001
+ try {
2002
+ const fn = new AsyncFunction("ctx", stepDef.code);
2003
+ compiledSteps.set(key, fn);
2004
+ return fn;
2005
+ } catch (error) {
2006
+ const errorMsg = error instanceof Error ? error.message : String(error);
2007
+ const codePreview = stepDef.code.length > 200 ? `${stepDef.code.substring(0, 200)}...` : stepDef.code;
2008
+ throw new Error(
2009
+ `Failed to compile code for step '${stepDef.id}' in workflow '${workflowId}':
2010
+ Error: ${errorMsg}
2011
+ Code:
2012
+ ${codePreview.split("\n").map((line) => ` ${line}`).join("\n")}`
2013
+ );
2014
+ }
2015
+ }
2016
+ async function executeStepWithAgent(stepDef, workflowId, input, state, context, options, runInternal) {
2017
+ const tools = context.tools;
2018
+ if (typeof tools.generateText !== "function" || typeof tools.invokeTool !== "function" || typeof tools.taskEvent !== "function") {
2019
+ throw new Error(
2020
+ `Step '${stepDef.id}' in workflow '${workflowId}' requires agent execution, but AgentToolRegistry tools are not available.`
2021
+ );
2022
+ }
2023
+ if (!options.toolInfo) {
2024
+ throw new Error(
2025
+ `Step '${stepDef.id}' in workflow '${workflowId}' requires agent execution, but no toolInfo was provided to DynamicWorkflowRunner.`
2026
+ );
2027
+ }
2028
+ const allowedToolNames = stepDef.tools;
2029
+ const toolsForAgent = allowedToolNames ? options.toolInfo.filter((t) => allowedToolNames.includes(t.name)) : [...options.toolInfo];
2030
+ if (!allowedToolNames || allowedToolNames.includes("runWorkflow")) {
2031
+ toolsForAgent.push({
2032
+ name: "runWorkflow",
2033
+ description: "Run a named sub-workflow defined in the current workflow file.",
2034
+ parameters: z22.object({
2035
+ workflowId: z22.string().describe("Sub-workflow id to run"),
2036
+ input: z22.any().nullish().describe("Optional input object for the sub-workflow")
2037
+ }),
2038
+ handler: async () => {
2039
+ return { type: "Error" /* Error */, message: { type: "error-text", value: "runWorkflow is virtual." } };
2040
+ }
2041
+ });
2042
+ }
2043
+ const allowedToolNameSet = new Set(toolsForAgent.map((t) => t.name));
2044
+ context.logger.debug(`[Agent] Available tools for step '${stepDef.id}': ${toolsForAgent.map((t) => t.name).join(", ")}`);
2045
+ const systemPrompt = options.stepSystemPrompt?.({ workflowId, step: stepDef, input, state }) ?? [
2046
+ `You are an AI assistant executing a workflow step.`,
2047
+ "",
2048
+ "# Instructions",
2049
+ "- Execute the task defined in the user message.",
2050
+ "- Use the provided tools to accomplish the task.",
2051
+ "- Return the step output as valid JSON in markdown.",
2052
+ "- Do not ask for user input. If information is missing, make a reasonable assumption or fail."
2053
+ ].filter(Boolean).join("\n");
2054
+ const userContent = [
2055
+ `Workflow: ${workflowId}`,
2056
+ `Step: ${stepDef.id}`,
2057
+ `Task: ${stepDef.task}`,
2058
+ stepDef.expected_outcome ? `Expected outcome: ${stepDef.expected_outcome}` : "",
2059
+ `Workflow Input: ${JSON.stringify(input)}`,
2060
+ `Current State: ${JSON.stringify(state)}`
2061
+ ].filter(Boolean).join("\n");
2062
+ const runWorkflow = createRunWorkflowFn({ input, state, context, runInternal });
2063
+ const agentTools = {
2064
+ generateText: tools.generateText.bind(tools),
2065
+ taskEvent: tools.taskEvent.bind(tools),
2066
+ invokeTool: async ({ toolName, input: toolInput }) => {
2067
+ if (!allowedToolNameSet.has(toolName)) {
2068
+ return {
2069
+ type: "Error" /* Error */,
2070
+ message: { type: "error-text", value: `Tool '${toolName}' is not allowed in this step.` }
2071
+ };
2072
+ }
2073
+ if (toolName === "runWorkflow") {
2074
+ const subWorkflowId = toolInput?.workflowId;
2075
+ const subInput = toolInput?.input;
2076
+ if (typeof subWorkflowId !== "string") {
2077
+ return {
2078
+ type: "Error" /* Error */,
2079
+ message: { type: "error-text", value: "runWorkflow.workflowId must be a string." }
2080
+ };
2081
+ }
2082
+ try {
2083
+ const output = await runWorkflow(subWorkflowId, subInput);
2084
+ const jsonResult = { type: "json", value: output };
2085
+ return { type: "Reply" /* Reply */, message: jsonResult };
2086
+ } catch (error) {
2087
+ return {
2088
+ type: "Error" /* Error */,
2089
+ message: { type: "error-text", value: error instanceof Error ? error.message : String(error) }
2090
+ };
2091
+ }
2092
+ }
2093
+ return await tools.invokeTool({ toolName, input: toolInput });
2094
+ }
2095
+ };
2096
+ const result = await agentWorkflow(
2097
+ {
2098
+ tools: toolsForAgent,
2099
+ systemPrompt,
2100
+ userMessage: [{ role: "user", content: userContent }],
2101
+ maxToolRoundTrips: options.maxToolRoundTrips,
2102
+ model: options.model
2103
+ },
2104
+ { ...context, tools: agentTools }
2105
+ );
2106
+ if (result.type === "Exit") {
2107
+ if (result.object !== void 0) {
2108
+ return result.object;
2109
+ }
2110
+ const parsed = parseJsonFromMarkdown(result.message);
2111
+ if (parsed.success) {
2112
+ return parsed.data;
2113
+ }
2114
+ if (options.wrapAgentResultInObject) {
2115
+ context.logger.warn(`[Agent] Step '${stepDef.id}' returned plain text instead of JSON. Wrapping in {result: ...}`);
2116
+ return { result: result.message };
2117
+ }
2118
+ return result.message;
2119
+ }
2120
+ if (result.type === "Error") {
2121
+ throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' failed: ${result.error?.message || "Unknown error"}`);
2122
+ }
2123
+ if (result.type === "UsageExceeded") {
2124
+ throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' exceeded usage limits (tokens or rounds)`);
2125
+ }
2126
+ throw new Error(`Agent step '${stepDef.id}' in workflow '${workflowId}' exited unexpectedly with type: ${result.type}`);
2127
+ }
2128
+ async function executeStepWithTimeout(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal) {
2129
+ const executeStepLogic = async () => {
2130
+ if (stepDef.code && options.allowUnsafeCodeExecution) {
2131
+ context.logger.debug(`[Step] Executing step '${stepDef.id}' with compiled code`);
2132
+ const fn = compileStep(stepDef, workflowId, compiledSteps);
2133
+ const runWorkflow = createRunWorkflowFn({ input, state, context, runInternal });
2134
+ const runtimeCtx = {
2135
+ workflowId,
2136
+ stepId: stepDef.id,
2137
+ input,
2138
+ state,
2139
+ tools: context.tools,
2140
+ logger: context.logger,
2141
+ step: context.step,
2142
+ runWorkflow,
2143
+ toolInfo: options.toolInfo
2144
+ };
2145
+ const result2 = await fn(runtimeCtx);
2146
+ context.logger.debug(`[Step] Compiled code execution completed for step '${stepDef.id}'`);
2147
+ return result2;
2148
+ }
2149
+ context.logger.debug(`[Step] Executing step '${stepDef.id}' with agent`);
2150
+ const result = await executeStepWithAgent(stepDef, workflowId, input, state, context, options, runInternal);
2151
+ context.logger.debug(`[Step] Agent execution completed for step '${stepDef.id}'`);
2152
+ return result;
2153
+ };
2154
+ if (stepDef.timeout && stepDef.timeout > 0) {
2155
+ context.logger.debug(`[Step] Step '${stepDef.id}' has timeout of ${stepDef.timeout}ms`);
2156
+ let timeoutId;
2157
+ const timeoutPromise = new Promise((_, reject) => {
2158
+ timeoutId = setTimeout(
2159
+ () => reject(new Error(`Step '${stepDef.id}' in workflow '${workflowId}' timed out after ${stepDef.timeout}ms`)),
2160
+ stepDef.timeout
2161
+ );
2162
+ });
2163
+ try {
2164
+ return await Promise.race([executeStepLogic(), timeoutPromise]);
2165
+ } finally {
2166
+ if (timeoutId) clearTimeout(timeoutId);
2167
+ }
2168
+ }
2169
+ return await executeStepLogic();
2170
+ }
2171
+ async function executeStep(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal) {
2172
+ const result = await executeStepWithTimeout(stepDef, workflowId, input, state, context, options, compiledSteps, runInternal);
2173
+ if (stepDef.outputSchema) {
2174
+ try {
2175
+ const _schema = z22.any();
2176
+ if (typeof stepDef.outputSchema === "object") {
2177
+ context.logger.debug(`[Step] Validating output for step '${stepDef.id}' against schema`);
2178
+ if (stepDef.outputSchema.type === "object") {
2179
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
2180
+ throw new Error(`Expected object output, got ${Array.isArray(result) ? "array" : result === null ? "null" : typeof result}`);
2181
+ }
2182
+ }
2183
+ if (stepDef.outputSchema.type === "array" && !Array.isArray(result)) {
2184
+ throw new Error(`Expected array output, got ${typeof result}`);
2185
+ }
2186
+ }
2187
+ } catch (error) {
2188
+ throw new Error(
2189
+ `Step '${stepDef.id}' in workflow '${workflowId}' output validation failed: ${error instanceof Error ? error.message : String(error)}`
2190
+ );
2191
+ }
2192
+ }
2193
+ return result;
2194
+ }
2195
+ function createDynamicWorkflow(definition, options = {}) {
2196
+ if (typeof definition === "string") {
2197
+ const res = parseDynamicWorkflowDefinition(definition);
2198
+ if (!res.success) {
2199
+ throw new Error(res.error);
2200
+ }
2201
+ definition = res.definition;
2202
+ }
2203
+ const compiledSteps = /* @__PURE__ */ new Map();
2204
+ const runInternal = async (workflowId, input, context, inheritedState) => {
2205
+ const workflow = definition.workflows[workflowId];
2206
+ if (!workflow) {
2207
+ throw new Error(`Workflow '${workflowId}' not found`);
2208
+ }
2209
+ const validatedInput = validateAndApplyDefaults(workflowId, workflow, input);
2210
+ context.logger.info(`[Workflow] Starting workflow '${workflowId}'`);
2211
+ context.logger.debug(`[Workflow] Input: ${JSON.stringify(validatedInput)}`);
2212
+ context.logger.debug(`[Workflow] Inherited state: ${JSON.stringify(inheritedState)}`);
2213
+ context.logger.debug(`[Workflow] Steps: ${workflow.steps.map((s) => s.id).join(", ")}`);
2214
+ const state = { ...inheritedState };
2215
+ let lastOutput;
2216
+ for (let i = 0; i < workflow.steps.length; i++) {
2217
+ const stepDef = workflow.steps[i];
2218
+ const stepName = `${workflowId}.${stepDef.id}`;
2219
+ context.logger.info(`[Workflow] Step ${i + 1}/${workflow.steps.length}: ${stepDef.id}`);
2220
+ context.logger.debug(`[Workflow] Step task: ${stepDef.task}`);
2221
+ if (stepDef.expected_outcome) {
2222
+ context.logger.debug(`[Workflow] Expected outcome: ${stepDef.expected_outcome}`);
2223
+ }
2224
+ context.logger.debug(`[Workflow] Current state keys: ${Object.keys(state).join(", ")}`);
2225
+ lastOutput = await context.step(stepName, async () => {
2226
+ return await executeStep(stepDef, workflowId, validatedInput, state, context, options, compiledSteps, runInternal);
2227
+ });
2228
+ const outputKey = stepDef.output ?? stepDef.id;
2229
+ state[outputKey] = lastOutput;
2230
+ context.logger.debug(
2231
+ `[Workflow] Step output stored as '${outputKey}': ${typeof lastOutput === "object" ? JSON.stringify(lastOutput).substring(0, 200) : lastOutput}`
2232
+ );
2233
+ }
2234
+ context.logger.info(`[Workflow] Completed workflow '${workflowId}'`);
2235
+ if (workflow.output) {
2236
+ context.logger.debug(`[Workflow] Returning output field: ${workflow.output}`);
2237
+ return state[workflow.output];
2238
+ }
2239
+ context.logger.debug(`[Workflow] Returning full state with keys: ${Object.keys(state).join(", ")}`);
2240
+ return state;
2241
+ };
2242
+ return async (workflowId, input, context) => {
2243
+ return await runInternal(workflowId, input, context, {});
2244
+ };
2245
+ }
2246
+
2247
+ // src/workflow/dynamic-generator.workflow.ts
2248
+ import { z as z23 } from "zod";
2249
+ var GenerateWorkflowDefinitionInputSchema = z23.object({
2250
+ prompt: z23.string(),
2251
+ availableTools: z23.array(
2252
+ z23.object({
2253
+ name: z23.string(),
2254
+ description: z23.string()
2255
+ })
2256
+ ).optional()
2257
+ });
2258
+ var GenerateWorkflowCodeInputSchema = z23.object({
2259
+ workflow: WorkflowFileSchema
2260
+ });
2261
+ var WORKFLOW_DEFINITION_SYSTEM_PROMPT = `You are an expert workflow architect.
2262
+ Your task is to create a JSON workflow definition based on the user's request.
2263
+
2264
+ The workflow definition must follow this structure:
2265
+ {
2266
+ "workflows": {
2267
+ "workflowName": {
2268
+ "task": "Description of the workflow",
2269
+ "inputs": [
2270
+ { "id": "inputName", "description": "Description", "default": "optionalDefault" }
2271
+ ],
2272
+ "steps": [
2273
+ {
2274
+ "id": "stepId",
2275
+ "task": "Description of the step",
2276
+ "tools": ["toolName1", "toolName2"], // Optional: restrict which tools can be used
2277
+ "output": "outputVariableName", // Optional: defaults to step id
2278
+ "timeout": 30000, // Optional: timeout in milliseconds
2279
+ "expected_outcome": "What this step produces", // Optional: documentation
2280
+ "outputSchema": { "type": "object" } // Optional: validation schema
2281
+ }
2282
+ ],
2283
+ "output": "outputVariableName" // Optional
2284
+ }
2285
+ }
2286
+ }
2287
+
2288
+ Constraints:
2289
+ - You MUST always include a workflow named 'main'. This is the entry point.
2290
+ - The 'main' workflow input must be either empty (no input) or a single string input.
2291
+ - Break down complex tasks into logical steps.
2292
+ - Define clear inputs and outputs.
2293
+
2294
+ Quality Guidelines:
2295
+ - Add "timeout" field (in milliseconds) for steps that might take long (file I/O, API calls, searches)
2296
+ - Use "expected_outcome" field to document what each step should produce
2297
+ - Use descriptive step IDs (e.g., "validateInput", "fetchUserData", not "step1", "step2")
2298
+ - Design steps to be focused - one responsibility per step
2299
+ - For steps that process multiple items, consider creating a sub-workflow
2300
+ - Add "outputSchema" with type information for validation-critical steps
2301
+ - Order steps logically with clear data flow
2302
+
2303
+ Example 1:
2304
+ User: "Research a topic and summarize it."
2305
+ Output:
2306
+ \`\`\`json
2307
+ {
2308
+ "workflows": {
2309
+ "main": {
2310
+ "task": "Research a topic and provide a summary",
2311
+ "inputs": [
2312
+ { "id": "topic", "description": "The topic to research" }
2313
+ ],
2314
+ "steps": [
2315
+ {
2316
+ "id": "search",
2317
+ "task": "Search for information about the topic",
2318
+ "tools": ["search"],
2319
+ "output": "searchResults"
2320
+ },
2321
+ {
2322
+ "id": "summarize",
2323
+ "task": "Summarize the search results",
2324
+ "tools": ["generateText"],
2325
+ "output": "summary"
2326
+ }
2327
+ ],
2328
+ "output": "summary"
2329
+ }
2330
+ }
2331
+ }
2332
+ \`\`\`
2333
+
2334
+ Example 2:
2335
+ User: "Review urgent PRs. For each PR, run the review workflow."
2336
+ Output:
2337
+ \`\`\`json
2338
+ {
2339
+ "workflows": {
2340
+ "main": {
2341
+ "task": "Fetch urgent PRs and review them",
2342
+ "inputs": [],
2343
+ "steps": [
2344
+ {
2345
+ "id": "fetchPRs",
2346
+ "task": "Fetch list of urgent PRs",
2347
+ "tools": ["github_list_prs"],
2348
+ "output": "prs"
2349
+ },
2350
+ {
2351
+ "id": "reviewEachPR",
2352
+ "task": "Run review workflow for each PR",
2353
+ "tools": [],
2354
+ "output": "reviews"
2355
+ }
2356
+ ],
2357
+ "output": "reviews"
2358
+ },
2359
+ "reviewPR": {
2360
+ "task": "Review a single PR",
2361
+ "inputs": [
2362
+ { "id": "prId", "description": "ID of the PR to review" }
2363
+ ],
2364
+ "steps": [
2365
+ {
2366
+ "id": "getDiff",
2367
+ "task": "Get PR diff",
2368
+ "tools": ["github_get_diff"],
2369
+ "output": "diff"
2370
+ },
2371
+ {
2372
+ "id": "analyze",
2373
+ "task": "Analyze the diff",
2374
+ "tools": ["generateText"],
2375
+ "output": "analysis"
2376
+ }
2377
+ ],
2378
+ "output": "analysis"
2379
+ }
2380
+ }
2381
+ }
2382
+ \`\`\`
2383
+ `;
2384
+ var WORKFLOW_CODE_SYSTEM_PROMPT = `You are an expert TypeScript developer.
2385
+ Your task is to implement the TypeScript code for the steps in the provided workflow definition.
2386
+
2387
+ You will receive a JSON workflow definition where the "code" field is null.
2388
+ You must fill in the "code" field for each step with valid TypeScript code.
2389
+
2390
+ CRITICAL: Each step "code" field must contain ONLY the function body statements (the code inside the curly braces).
2391
+ DO NOT include function declaration, arrow function syntax, async keyword, parameter list, or outer curly braces.
2392
+
2393
+ The code will be wrapped automatically in: \`async (ctx) => { YOUR_CODE_HERE }\`
2394
+
2395
+ Example of CORRECT code field:
2396
+ \`\`\`ts
2397
+ const result = await ctx.tools.readFile({ path: 'README.md' })
2398
+ if (!result) throw new Error('File not found')
2399
+ return result
2400
+ \`\`\`
2401
+
2402
+ Example of INCORRECT code field (DO NOT DO THIS):
2403
+ \`\`\`ts
2404
+ async (ctx) => {
2405
+ const result = await ctx.tools.readFile({ path: 'README.md' })
2406
+ return result
2407
+ }
2408
+ \`\`\`
2409
+
2410
+ Example of INCORRECT code field (DO NOT DO THIS):
2411
+ \`\`\`ts
2412
+ (ctx) => {
2413
+ return 'hello'
2414
+ }
2415
+ \`\`\`
2416
+
2417
+ ## Runtime context (ctx)
2418
+ \`\`\`ts
2419
+ // Runtime types (for reference)
2420
+ type Logger = {
2421
+ debug: (...args: any[]) => void
2422
+ info: (...args: any[]) => void
2423
+ warn: (...args: any[]) => void
2424
+ error: (...args: any[]) => void
2425
+ }
2426
+
2427
+ type StepFn = {
2428
+ <T>(name: string, fn: () => Promise<T>): Promise<T>
2429
+ <T>(name: string, options: { retry?: number }, fn: () => Promise<T>): Promise<T>
2430
+ }
2431
+
2432
+ type JsonModelMessage = { role: 'system' | 'user' | 'assistant' | 'tool'; content: any }
2433
+ type JsonResponseMessage = { role: 'assistant' | 'tool'; content: any }
2434
+ type ToolSet = Record<string, any>
2435
+
2436
+ type ToolResponseResult =
2437
+ | { type: 'text'; value: string }
2438
+ | { type: 'json'; value: any }
2439
+ | { type: 'error-text'; value: string }
2440
+ | { type: 'error-json'; value: any }
2441
+ | { type: 'content'; value: any[] }
2442
+
2443
+ type AgentToolResponse =
2444
+ | { type: 'Reply'; message: ToolResponseResult }
2445
+ | { type: 'Exit'; message: string; object?: any }
2446
+ | { type: 'Error'; message: ToolResponseResult }
2447
+
2448
+ type ExitReason =
2449
+ | { type: 'UsageExceeded' }
2450
+ | { type: 'Exit'; message: string; object?: any }
2451
+ | { type: 'Error'; error: { message: string; stack?: string } }
2452
+
2453
+ type FullAgentToolInfo = { name: string; description: string; parameters: any; handler: any }
2454
+
2455
+ // Tools available on ctx.tools in dynamic steps
2456
+ type DynamicWorkflowTools = {
2457
+ // LLM + agent helpers
2458
+ generateText: (input: { messages: JsonModelMessage[]; tools: ToolSet }) => Promise<JsonResponseMessage[]>
2459
+ runAgent: (input: {
2460
+ tools: Readonly<FullAgentToolInfo[]>
2461
+ maxToolRoundTrips?: number
2462
+ userMessage: readonly JsonModelMessage[]
2463
+ } & ({ messages: JsonModelMessage[] } | { systemPrompt: string })) => Promise<ExitReason>
2464
+
2465
+ // Generic bridge to "agent tools" by name
2466
+ invokeTool: (input: { toolName: string; input: any }) => Promise<AgentToolResponse>
2467
+
2468
+ // File + command helpers (direct)
2469
+ readFile: (input: { path: string }) => Promise<string | null>
2470
+ writeToFile: (input: { path: string; content: string }) => Promise<void>
2471
+ executeCommand: (input: { command: string; pipe?: boolean } & ({ args: string[]; shell?: false } | { shell: true })) => Promise<{
2472
+ exitCode: number
2473
+ stdout: string
2474
+ stderr: string
2475
+ }>
2476
+
2477
+ // CLI UX helpers
2478
+ confirm: (input: { message: string }) => Promise<boolean>
2479
+ input: (input: { message: string; default?: string }) => Promise<string>
2480
+ select: (input: { message: string; choices: { name: string; value: string }[] }) => Promise<string>
2481
+ }
2482
+
2483
+ type DynamicStepRuntimeContext = {
2484
+ workflowId: string
2485
+ stepId: string
2486
+ input: Record<string, any>
2487
+ state: Record<string, any>
2488
+ tools: DynamicWorkflowTools
2489
+ logger: Logger
2490
+ step: StepFn
2491
+ runWorkflow: (workflowId: string, input?: Record<string, any>) => Promise<any>
2492
+ toolInfo?: ReadonlyArray<FullAgentToolInfo>
2493
+ }
2494
+ \`\`\`
2495
+
2496
+ - \`ctx.input\`: workflow inputs (read-only).
2497
+ - \`ctx.state\`: shared state between steps (previous step outputs are stored here).
2498
+ - \`ctx.tools\`: async tool functions. Call tools as \`await ctx.tools.someTool({ ... })\`.
2499
+ - \`ctx.runWorkflow\`: run a sub-workflow by id.
2500
+
2501
+ ## Guidelines
2502
+ - Use \`await\` for all async operations.
2503
+ - Return the output value for the step (this becomes the step output).
2504
+ - Access inputs via \`ctx.input.<inputId>\`.
2505
+ - Access previous step outputs via \`ctx.state.<stepOutputKey>\` (defaults to the step \`output\` or \`id\`).
2506
+
2507
+ ## Quality Guidelines for Code Implementation
2508
+
2509
+ ### Error Handling
2510
+ - ALWAYS validate inputs at the start of steps
2511
+ - Use try-catch for operations that might fail (file I/O, parsing, API calls)
2512
+ - Preserve stack traces: re-throw original errors rather than creating new ones
2513
+ - Use error type guards: \`const err = error instanceof Error ? error : new Error(String(error))\`
2514
+ - Check for null/undefined before using values
2515
+ - Handle edge cases (empty arrays, missing files, invalid data)
2516
+
2517
+ ### Logging
2518
+ - Use \`ctx.logger.info()\` for important progress updates
2519
+ - Use \`ctx.logger.debug()\` for detailed information
2520
+ - Use \`ctx.logger.warn()\` for recoverable issues
2521
+ - Use \`ctx.logger.error()\` before throwing errors
2522
+ - Log when starting and completing significant operations
2523
+ - Use template literals for readability: \`ctx.logger.info(\\\`Processing \${items.length} items...\\\`)\`
2524
+
2525
+ ### User Experience
2526
+ - Provide progress feedback for long operations
2527
+ - Return structured data (objects/arrays), not strings when possible
2528
+ - Include helpful metadata in results (counts, timestamps, status)
2529
+ - For batch operations, report progress: \`Processed 5/10 items\`
2530
+
2531
+ ### Data Validation
2532
+ - Validate required fields exist before accessing
2533
+ - Check data types match expectations
2534
+ - Validate array lengths before iteration
2535
+ - Example: \`if (!data?.users || !Array.isArray(data.users)) throw new Error('Invalid data format')\`
2536
+
2537
+ ### Best Practices
2538
+ - Use meaningful variable names
2539
+ - Avoid nested callbacks - use async/await
2540
+ - Clean up resources (close files, clear timeouts)
2541
+ - Return consistent data structures across similar steps
2542
+ - For iteration, consider batching or rate limiting
2543
+
2544
+ ### When to Simplify
2545
+ - Simple transformation steps (e.g., formatting strings) need only basic error handling
2546
+ - Internal sub-workflow steps with validated inputs from parent can skip redundant validation
2547
+ - Minimal logging is fine for fast steps (<100ms) that don't perform I/O or external calls
2548
+ - Use judgment: match error handling complexity to the step's failure risk and impact
2549
+
2550
+ ## Tool calling examples (every tool)
2551
+
2552
+ ### Direct ctx.tools methods
2553
+ \`\`\`ts
2554
+ // readFile
2555
+ const readme = await ctx.tools.readFile({ path: 'README.md' })
2556
+ if (readme == null) throw new Error('README.md not found')
2557
+
2558
+ // writeToFile
2559
+ await ctx.tools.writeToFile({ path: 'notes.txt', content: 'hello\\n' })
2560
+
2561
+ // executeCommand (args form)
2562
+ const rg = await ctx.tools.executeCommand({ command: 'rg', args: ['-n', 'TODO', '.'] })
2563
+ if (rg.exitCode !== 0) throw new Error(rg.stderr)
2564
+
2565
+ // executeCommand (shell form)
2566
+ await ctx.tools.executeCommand({ command: 'ls -la', shell: true, pipe: true })
2567
+
2568
+ // generateText (LLM call; pass tools: {})
2569
+ const msgs = await ctx.tools.generateText({
2570
+ messages: [
2571
+ { role: 'system', content: 'Summarize the following text.' },
2572
+ { role: 'user', content: readme },
2573
+ ],
2574
+ tools: {},
2575
+ })
2576
+ const last = msgs[msgs.length - 1]
2577
+ const lastText = typeof last?.content === 'string' ? last.content : JSON.stringify(last?.content)
2578
+
2579
+ // runAgent (nested agent; use ctx.toolInfo as the tool list)
2580
+ const agentRes = await ctx.tools.runAgent({
2581
+ systemPrompt: 'You are a helpful assistant.',
2582
+ userMessage: [{ role: 'user', content: 'Summarize README.md in 3 bullets.' }],
2583
+ tools: (ctx.toolInfo ?? []) as any,
2584
+ })
2585
+ if (agentRes.type !== 'Exit') throw new Error('runAgent failed')
2586
+
2587
+ // confirm / input / select (interactive)
2588
+ const ok = await ctx.tools.confirm({ message: 'Proceed?' })
2589
+ const name = await ctx.tools.input({ message: 'Name?', default: 'main' })
2590
+ const flavor = await ctx.tools.select({
2591
+ message: 'Pick one',
2592
+ choices: [
2593
+ { name: 'A', value: 'a' },
2594
+ { name: 'B', value: 'b' },
2595
+ ],
2596
+ })
2597
+
2598
+ \`\`\`
2599
+
2600
+ ### Agent tools via ctx.tools.invokeTool (toolName examples)
2601
+ \`\`\`ts
2602
+ // Helper to unwrap a successful tool reply
2603
+ function unwrapToolValue(resp: any) {
2604
+ if (!resp || resp.type !== 'Reply') {
2605
+ const msg = resp?.message?.value
2606
+ throw new Error(typeof msg === 'string' ? msg : JSON.stringify(resp))
2607
+ }
2608
+ return resp.message.value
2609
+ }
2610
+
2611
+ // askFollowupQuestion
2612
+ const answersText = unwrapToolValue(
2613
+ await ctx.tools.invokeTool({
2614
+ toolName: 'askFollowupQuestion',
2615
+ input: { questions: [{ prompt: 'Which directory?', options: ['src', 'packages'] }] },
2616
+ }),
2617
+ )
2618
+
2619
+ // listFiles
2620
+ const filesText = unwrapToolValue(
2621
+ await ctx.tools.invokeTool({
2622
+ toolName: 'listFiles',
2623
+ input: { path: 'src', recursive: true, maxCount: 2000, includeIgnored: false },
2624
+ }),
2625
+ )
2626
+
2627
+ // searchFiles
2628
+ const hitsText = unwrapToolValue(
2629
+ await ctx.tools.invokeTool({
2630
+ toolName: 'searchFiles',
2631
+ input: { path: '.', regex: 'generateWorkflowCodeWorkflow', filePattern: '*.ts' },
2632
+ }),
2633
+ )
2634
+
2635
+ // fetchUrl
2636
+ const pageText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'fetchUrl', input: { url: 'https://example.com' } }))
2637
+
2638
+ // search (web search)
2639
+ const webResults = unwrapToolValue(
2640
+ await ctx.tools.invokeTool({ toolName: 'search', input: { query: 'TypeScript zod schema examples' } }),
2641
+ )
2642
+
2643
+ // executeCommand (provider-backed; may require approval in some environments)
2644
+ const cmdText = unwrapToolValue(
2645
+ await ctx.tools.invokeTool({ toolName: 'executeCommand', input: { command: 'bun test', requiresApproval: false } }),
2646
+ )
2647
+
2648
+ // readFile / writeToFile (provider-backed)
2649
+ const fileText = unwrapToolValue(
2650
+ await ctx.tools.invokeTool({ toolName: 'readFile', input: { path: 'README.md', includeIgnored: false } }),
2651
+ )
2652
+ const writeText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'writeToFile', input: { path: 'out.txt', content: 'hi' } }))
2653
+
2654
+ // replaceInFile
2655
+ const diff = ['<<<<<<< SEARCH', 'old', '=======', 'new', '>>>>>>> REPLACE'].join('\\n')
2656
+ const replaceText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'replaceInFile', input: { path: 'out.txt', diff } }))
2657
+
2658
+ // removeFile / renameFile
2659
+ const rmText = unwrapToolValue(await ctx.tools.invokeTool({ toolName: 'removeFile', input: { path: 'out.txt' } }))
2660
+ const mvText = unwrapToolValue(
2661
+ await ctx.tools.invokeTool({ toolName: 'renameFile', input: { source_path: 'a.txt', target_path: 'b.txt' } }),
2662
+ )
2663
+
2664
+ // readBinaryFile (returns { type: 'content', value: [...] } in resp.message)
2665
+ const binResp = await ctx.tools.invokeTool({ toolName: 'readBinaryFile', input: { url: 'file://path/to/image.png' } })
2666
+ \`\`\`
2667
+
2668
+ ### Sub-workflow example (ctx.runWorkflow)
2669
+ \`\`\`ts
2670
+ const results: any[] = []
2671
+ for (const pr of ctx.state.prs ?? []) {
2672
+ results.push(await ctx.runWorkflow('reviewPR', { prId: pr.id }))
2673
+ }
2674
+ return results
2675
+ \`\`\`
2676
+
2677
+ ## Complete Example: High-Quality Step Implementation
2678
+
2679
+ This example demonstrates all quality guidelines in a single step:
2680
+
2681
+ \`\`\`ts
2682
+ // Step: processUserData
2683
+ // Task: Read, validate, and process user data from a file
2684
+
2685
+ // Input validation
2686
+ if (!ctx.input.dataFile) {
2687
+ throw new Error('Missing required input: dataFile')
2688
+ }
2689
+
2690
+ ctx.logger.info(\`Starting user data processing for: \${ctx.input.dataFile}\`)
2691
+
2692
+ // Read file with error handling
2693
+ let rawData
2694
+ try {
2695
+ ctx.logger.debug(\`Reading file: \${ctx.input.dataFile}\`)
2696
+ rawData = await ctx.tools.readFile({ path: ctx.input.dataFile })
2697
+
2698
+ if (!rawData) {
2699
+ throw new Error(\`File not found or empty: \${ctx.input.dataFile}\`)
2700
+ }
2701
+ } catch (error) {
2702
+ const err = error instanceof Error ? error : new Error(String(error))
2703
+ ctx.logger.error(\`Failed to read file: \${err.message}\`)
2704
+ throw err // Preserve original stack trace
2705
+ }
2706
+
2707
+ // Parse and validate data
2708
+ let users
2709
+ try {
2710
+ ctx.logger.debug('Parsing JSON data')
2711
+ const parsed = JSON.parse(rawData)
2712
+
2713
+ if (!parsed?.users || !Array.isArray(parsed.users)) {
2714
+ throw new Error('Invalid data format: expected {users: [...]}')
2715
+ }
2716
+
2717
+ users = parsed.users
2718
+ ctx.logger.info(\`Found \${users.length} users to process\`)
2719
+ } catch (error) {
2720
+ const err = error instanceof Error ? error : new Error(String(error))
2721
+ ctx.logger.error(\`Data parsing failed: \${err.message}\`)
2722
+ throw err // Preserve original stack trace
2723
+ }
2724
+
2725
+ // Process each user with progress reporting
2726
+ const results = []
2727
+ for (let i = 0; i < users.length; i++) {
2728
+ const user = users[i]
2729
+
2730
+ // Validate each user object
2731
+ if (!user?.id || !user?.email) {
2732
+ ctx.logger.warn(\`Skipping invalid user at index \${i}: missing id or email\`)
2733
+ continue
2734
+ }
2735
+
2736
+ // Process user
2737
+ const processed = {
2738
+ id: user.id,
2739
+ email: user.email.toLowerCase().trim(),
2740
+ name: user.name?.trim() || 'Unknown',
2741
+ processedAt: new Date().toISOString(),
2742
+ status: 'active'
2743
+ }
2744
+
2745
+ results.push(processed)
2746
+
2747
+ // Progress feedback every 10 items
2748
+ if ((i + 1) % 10 === 0) {
2749
+ ctx.logger.info(\`Processed \${i + 1}/\${users.length} users\`)
2750
+ }
2751
+ }
2752
+
2753
+ ctx.logger.info(\`Successfully processed \${results.length}/\${users.length} users\`)
2754
+
2755
+ // Return structured result with metadata
2756
+ return {
2757
+ users: results,
2758
+ metadata: {
2759
+ totalInput: users.length,
2760
+ totalProcessed: results.length,
2761
+ skipped: users.length - results.length,
2762
+ processedAt: new Date().toISOString()
2763
+ }
2764
+ }
2765
+ \`\`\`
2766
+
2767
+ Key features demonstrated:
2768
+ - Input validation at start
2769
+ - Comprehensive error handling with try-catch that preserves stack traces
2770
+ - Logging at info, debug, warn, and error levels
2771
+ - Progress reporting for long operations (every 10 items)
2772
+ - Data validation throughout (null checks, type checks, array validation)
2773
+ - Structured return value with metadata for observability
2774
+ - Descriptive error messages with context
2775
+ - Meaningful variable names (rawData, users, processed)
2776
+ - Clean async/await usage
2777
+ - Template literals for readable string interpolation
2778
+ - Proper error type guards (error instanceof Error)
2779
+
2780
+ ## Final Instructions
2781
+
2782
+ REMEMBER: The "code" field must be ONLY the function body statements.
2783
+ - DO NOT wrap code in arrow functions: \`(ctx) => { ... }\`
2784
+ - DO NOT wrap code in async functions: \`async (ctx) => { ... }\`
2785
+ - DO NOT include outer curly braces
2786
+ - DO include a return statement if the step should produce output
2787
+ - Each "code" field should be a string containing multiple statements separated by newlines
2788
+
2789
+ Return the complete workflow JSON with the "code" fields populated.
2790
+ `;
2791
+ var generateWorkflowDefinitionWorkflow = async (input, ctx) => {
2792
+ let systemPrompt = WORKFLOW_DEFINITION_SYSTEM_PROMPT;
2793
+ if (input.availableTools && input.availableTools.length > 0) {
2794
+ const toolsList = input.availableTools.map((t) => `- ${t.name}: ${t.description}`).join("\n");
2795
+ systemPrompt += `
2796
+
2797
+ Available Tools:
2798
+ ${toolsList}
2799
+
2800
+ Use these tools when appropriate.`;
2801
+ }
2802
+ const result = await ctx.step("generate-workflow-definition", async () => {
2803
+ return agentWorkflow(
2804
+ {
2805
+ systemPrompt,
2806
+ userMessage: [{ role: "user", content: input.prompt }],
2807
+ tools: [],
2808
+ outputSchema: WorkflowFileSchema
2809
+ },
2810
+ ctx
2811
+ );
2812
+ });
2813
+ if (result.type === "Exit" && result.object) {
2814
+ return result.object;
2815
+ }
2816
+ throw new Error("Failed to generate workflow definition");
2817
+ };
2818
+ var generateWorkflowCodeWorkflow = async (input, ctx) => {
2819
+ const result = await ctx.step("generate-workflow-code", async () => {
2820
+ return agentWorkflow(
2821
+ {
2822
+ systemPrompt: WORKFLOW_CODE_SYSTEM_PROMPT,
2823
+ userMessage: [{ role: "user", content: JSON.stringify(input.workflow, null, 2) }],
2824
+ tools: [],
2825
+ outputSchema: WorkflowFileSchema
2826
+ },
2827
+ ctx
2828
+ );
2829
+ });
2830
+ if (result.type === "Exit" && result.object) {
2831
+ return result.object;
2832
+ }
2833
+ throw new Error("Failed to generate workflow code");
2834
+ };
2835
+
1907
2836
  // src/workflow/json-ai-types.ts
1908
2837
  var toJsonDataContent = (data) => {
1909
2838
  if (data instanceof URL) {
@@ -2102,6 +3031,8 @@ var makeStepFn = () => {
2102
3031
  };
2103
3032
  };
2104
3033
  export {
3034
+ GenerateWorkflowCodeInputSchema,
3035
+ GenerateWorkflowDefinitionInputSchema,
2105
3036
  MockProvider,
2106
3037
  TaskEventKind,
2107
3038
  TodoItemSchema,
@@ -2110,19 +3041,27 @@ export {
2110
3041
  UpdateTodoItemInputSchema,
2111
3042
  UpdateTodoItemOutputSchema,
2112
3043
  UsageMeter,
3044
+ WorkflowDefinitionSchema,
3045
+ WorkflowFileSchema,
3046
+ WorkflowInputDefinitionSchema,
3047
+ WorkflowStepDefinitionSchema,
2113
3048
  agentWorkflow,
2114
3049
  askFollowupQuestion_default as askFollowupQuestion,
2115
3050
  computeRateLimitBackoffSeconds,
2116
3051
  configSchema,
2117
3052
  createContext,
3053
+ createDynamicWorkflow,
2118
3054
  executeCommand_default as executeCommand,
2119
3055
  fetchUrl_default as fetchUrl,
2120
3056
  fromJsonModelMessage,
3057
+ generateWorkflowCodeWorkflow,
3058
+ generateWorkflowDefinitionWorkflow,
2121
3059
  getTodoItem_default as getTodoItem,
2122
3060
  listFiles_default as listFiles,
2123
3061
  listMemoryTopics_default as listMemoryTopics,
2124
3062
  listTodoItems_default as listTodoItems,
2125
3063
  makeStepFn,
3064
+ parseDynamicWorkflowDefinition,
2126
3065
  parseJsonFromMarkdown,
2127
3066
  providerModelSchema,
2128
3067
  readBinaryFile_default as readBinaryFile,