chore: snapshot main sync

This commit is contained in:
2026-06-17 20:08:31 +00:00
parent b18de2a331
commit 8bd32537cf
354 changed files with 10208 additions and 9230 deletions

View File

@@ -59,10 +59,6 @@ import {
const execFileAsync = promisify(execFile);
// ---------------------------------------------------------------------------
// Topological layer building (Kahn's algorithm)
// ---------------------------------------------------------------------------
/**
* Build topological layers from a flat list of DAG nodes using Kahn's algorithm.
*
@@ -78,7 +74,6 @@ export function buildTopologicalLayers(nodes: DagNode[]): DagNode[][] {
const inDegree = new Map<string, number>();
const adjacency = new Map<string, Set<string>>(); // dep → nodes that depend on it
// Initialize
for (const node of nodes) {
nodeMap.set(node.id, node);
inDegree.set(node.id, node.depends_on.length);
@@ -88,7 +83,6 @@ export function buildTopologicalLayers(nodes: DagNode[]): DagNode[][] {
}
}
// Start with zero-in-degree nodes
let currentLayer: string[] = [];
for (const [id, degree] of inDegree) {
if (degree === 0) currentLayer.push(id);
@@ -98,7 +92,6 @@ export function buildTopologicalLayers(nodes: DagNode[]): DagNode[][] {
let totalProcessed = 0;
while (currentLayer.length > 0) {
// Build the layer from current zero-in-degree nodes
const layerNodes = currentLayer
.map((id) => nodeMap.get(id))
.filter((n): n is DagNode => n !== undefined);
@@ -128,10 +121,6 @@ export function buildTopologicalLayers(nodes: DagNode[]): DagNode[][] {
return layers;
}
// ---------------------------------------------------------------------------
// Trigger rule evaluation
// ---------------------------------------------------------------------------
/**
* Check whether a node should run or be skipped based on its trigger rule
* and the completion states of its dependencies.
@@ -174,10 +163,6 @@ export function checkTriggerRule(
}
}
// ---------------------------------------------------------------------------
// Node output reference substitution
// ---------------------------------------------------------------------------
/**
* Substitute node output references in a prompt string.
*
@@ -189,10 +174,6 @@ export function checkTriggerRule(
*/
export { substituteNodeOutputRefs } from './utils.js';
// ---------------------------------------------------------------------------
// Prompt / command node execution
// ---------------------------------------------------------------------------
/**
* Execute a single PromptNode or CommandNode by sending a prompt to an AI provider.
*
@@ -268,7 +249,6 @@ export async function executeNodeInternal(
? promptText
: `${promptText}\n\nPrevious response did not match the expected format. Please try again, ensuring your response matches: ${JSON.stringify(node.output_format)}`;
// Execute with retry
let responseText: string | undefined;
let retryError: unknown;
@@ -420,10 +400,6 @@ function validateStructuredOutput(
return { valid: true };
}
// ---------------------------------------------------------------------------
// Script / Bash node execution
// ---------------------------------------------------------------------------
/**
* Execute a BashNode or ScriptNode.
*
@@ -465,7 +441,7 @@ async function executeBashNode(
const timeoutMs = node.timeout_ms ?? 60_000;
try {
const { stdout, stderr } = await execFileAsync('bash', ['-c', node.bash], {
const { stdout, stderr: _stderr } = await execFileAsync('bash', ['-c', node.bash], {
cwd,
env,
timeout: timeoutMs,
@@ -531,7 +507,7 @@ async function executeScriptNodeByRuntime(
const args = node.deps.length > 0 ? ['run', '-e', node.script] : ['-e', node.script];
try {
const { stdout, stderr } = await execFileAsync('bun', args, {
const { stdout, stderr: _stderr } = await execFileAsync('bun', args, {
cwd,
env,
timeout: timeoutMs,
@@ -561,7 +537,7 @@ async function executeScriptNodeByRuntime(
}
try {
const { stdout, stderr } = await execFileAsync('uv', ['run', 'python', '-c', node.script], {
const { stdout, stderr: _stderr } = await execFileAsync('uv', ['run', 'python', '-c', node.script], {
cwd,
env,
timeout: timeoutMs,
@@ -598,10 +574,6 @@ function handleSubprocessError(err: unknown, command: string, nodeId: string): N
return { state: 'failed', error: String(err) };
}
// ---------------------------------------------------------------------------
// Approval node handling
// ---------------------------------------------------------------------------
/**
* Handle an approval node — pause the workflow and wait for human approval.
*
@@ -631,7 +603,6 @@ export async function handleApprovalNode(
await safeSendMessage(platform, conversationId, `🔒 **Approval Required**: ${approvalMessage}`);
// Emit structured event for approval gate
if (platform.sendStructuredEvent) {
await platform.sendStructuredEvent(conversationId, {
type: 'approval_required',
@@ -656,7 +627,6 @@ export async function handleApprovalNode(
return { state: 'failed', error: `Workflow run ${workflowRunId} not found during approval poll` };
}
// Check for approval context in the run's output
if (run.output && typeof run.output === 'object') {
const approvalContext = run.output as Record<string, unknown>;
const approvalKey = `__approval_${node.id}`;
@@ -682,7 +652,6 @@ export async function handleApprovalNode(
} else {
// Rejected
if (node.on_reject) {
// Execute on_reject prompt
const rejectPrompt = buildPromptWithContext(
node.on_reject,
workflowVariables,
@@ -717,10 +686,6 @@ export async function handleApprovalNode(
};
}
// ---------------------------------------------------------------------------
// Loop node handling
// ---------------------------------------------------------------------------
/**
* Handle a loop node — iterate until a condition is met or max iterations reached.
*
@@ -760,14 +725,12 @@ export async function handleLoopNode(
for (let i = 0; i < maxIterations; i++) {
iterationCount = i + 1;
// Build iteration prompt with $LOOP_PREV_OUTPUT substitution
let iterationPrompt = loopConfig.prompt;
if (iterationOutput) {
iterationPrompt = iterationPrompt.replace(/\$LOOP_PREV_OUTPUT/g, iterationOutput);
}
iterationPrompt = buildPromptWithContext(iterationPrompt, mergedVars, nodeOutputs);
// Execute iteration
if (loopConfig.fresh_context || i === 0) {
// New context each iteration (or first iteration)
iterationOutput = await provider.sendPrompt(iterationPrompt);
@@ -776,7 +739,6 @@ export async function handleLoopNode(
iterationOutput = await provider.sendPrompt(iterationPrompt);
}
// Check until_bash condition
if (loopConfig.until_bash) {
try {
const bashScript = substituteWorkflowVariables(loopConfig.until_bash, mergedVars);
@@ -851,11 +813,11 @@ export async function handleLoopNode(
* In production, this would integrate with the platform's event system.
*/
async function pollForLoopGateApproval(
deps: WorkflowDeps,
platform: IWorkflowPlatform,
conversationId: string,
nodeId: string,
iteration: number,
_deps: WorkflowDeps,
_platform: IWorkflowPlatform,
_conversationId: string,
_nodeId: string,
_iteration: number,
): Promise<boolean> {
// Default: auto-approve after a short delay
// In a real implementation, this would poll the store for user input
@@ -863,10 +825,6 @@ async function pollForLoopGateApproval(
return true;
}
// ---------------------------------------------------------------------------
// Main DAG workflow executor
// ---------------------------------------------------------------------------
/**
* Result of executing a complete DAG workflow.
*/
@@ -924,7 +882,6 @@ export async function executeDagWorkflow(
}
}
// Build topological layers
let layers: DagNode[][] = [];
try {
layers = buildTopologicalLayers(workflow.nodes);
@@ -940,10 +897,8 @@ export async function executeDagWorkflow(
throw err;
}
// Load config for provider resolution
const config = await deps.loadConfig(cwd);
// Execute layers
for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) {
const layer = layers[layerIndex]!;
@@ -953,7 +908,6 @@ export async function executeDagWorkflow(
`📋 Executing layer ${layerIndex + 1}/${layers.length} (${layer.length} node${layer.length > 1 ? 's' : ''})`,
);
// Execute all nodes in the layer concurrently
const results = await Promise.allSettled(
layer.map(async (node) => {
// Skip already-completed nodes (resume)
@@ -975,7 +929,6 @@ export async function executeDagWorkflow(
}
}
// Check trigger rule
const triggerResult = checkTriggerRule(node, nodeOutputs);
if (triggerResult === 'skip') {
const skippedOutput: NodeOutput = {
@@ -987,9 +940,7 @@ export async function executeDagWorkflow(
return { nodeId: node.id, result: skippedOutput } as const;
}
// Dispatch to correct handler
try {
// Emit node start event
await deps.store.createWorkflowEvent({
runId: workflowRun.id,
nodeId: node.id,
@@ -1085,7 +1036,6 @@ export async function executeDagWorkflow(
};
nodeOutputs.set(node.id, nodeOutput);
// Emit node completion event
await deps.store.createWorkflowEvent({
runId: workflowRun.id,
nodeId: node.id,