chore: add ion package, codesight wiki, work plans, ascli config

New @boocode/ion package (v0.0.1) for inference optimization network.
.codesight/ wiki artifacts for codebase documentation.
.omo/ work plans for openspec cleanup and enhanced file panel.
This commit is contained in:
2026-06-07 22:16:45 +00:00
parent ec48066a80
commit 02063072ab
63 changed files with 14025 additions and 0 deletions

View File

@@ -0,0 +1,373 @@
/**
* Top-level workflow executor for the Ion engine.
*
* Orchestrates the full lifecycle of a workflow run:
* - Load configuration and resolve provider/model
* - Create or resume a WorkflowRun
* - Path-lock guard (prevent concurrent runs on the same working path)
* - Pre-create artifacts directory
* - Delegate to executeDagWorkflow for DAG traversal
* - Update run status on completion/failure
* - Emit events and notify the user
*/
import { mkdir } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import type { WorkflowDefinition } from '../schema/index.js';
import type {
IWorkflowPlatform,
IWorkflowStore,
WorkflowDeps,
WorkflowConfig,
WorkflowRun,
CreateWorkflowRunData,
} from './deps.js';
import { executeDagWorkflow, type DagWorkflowResult } from './dag-executor.js';
import { safeSendMessage } from './utils.js';
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
/** Options for workflow execution. */
export interface WorkflowExecutionOptions {
/** Whether to resume a previously failed/paused run. */
resume?: boolean;
/** Additional input variables for the workflow. */
input?: Record<string, unknown>;
/** Override the provider id. */
provider?: string;
/** Override the model. */
model?: string;
/** Codebase id for scoped paths. */
codebaseId?: string;
}
/** Result of a workflow execution. */
export interface WorkflowExecutionResult {
/** The workflow run record. */
run: WorkflowRun;
/** The DAG execution result. */
dagResult?: DagWorkflowResult;
/** Whether the execution was successful. */
success: boolean;
/** Error message if execution failed. */
error?: string;
}
/** Hydrated resumable run data. */
export interface HydratedResumableRun {
/** The pre-created or resumed workflow run. */
preCreatedRun: WorkflowRun;
/** Prior completed node outputs from the previous run. */
priorCompletedNodes: Record<string, Record<string, unknown>>;
}
/** Resolved project paths for a workflow run. */
export interface ProjectPaths {
/** Directory for workflow artifacts. */
artifactsDir: string;
/** Directory for workflow logs. */
logDir: string;
}
// ---------------------------------------------------------------------------
// Main executor
// ---------------------------------------------------------------------------
/**
* Execute a workflow from start to finish.
*
* This is the primary entry point for running a workflow. It handles:
* 1. Loading configuration
* 2. Resolving provider and model
* 3. Creating or resuming a WorkflowRun
* 4. Path-lock guard
* 5. Pre-creating the artifacts directory
* 6. Delegating to executeDagWorkflow
* 7. Updating run status on completion/failure
*
* @param deps - Dependency injection container.
* @param platform - Platform interface for messaging.
* @param conversationId - Conversation channel id.
* @param cwd - Working directory for the workflow.
* @param workflow - The workflow definition.
* @param userMessage - The triggering user message (stored as input).
* @param opts - Execution options.
* @returns WorkflowExecutionResult with run, dag result, and success status.
*/
export async function executeWorkflow(
deps: WorkflowDeps,
platform: IWorkflowPlatform,
conversationId: string,
cwd: string,
workflow: WorkflowDefinition,
userMessage: string,
opts: WorkflowExecutionOptions = {},
): Promise<WorkflowExecutionResult> {
// 1. Load configuration
let config: WorkflowConfig;
try {
config = await deps.loadConfig(cwd);
} catch (err) {
return {
run: createFailedRun(workflow, err),
success: false,
error: `Failed to load configuration: ${err instanceof Error ? err.message : String(err)}`,
};
}
// 2. Resolve provider and model
const providerId = opts.provider ?? workflow.provider ?? config.assistant;
const model = opts.model ?? workflow.model ?? config.assistants[providerId]?.model;
// 3. Create or resume a workflow run
let workflowRun: WorkflowRun;
let priorCompletedNodes: Record<string, Record<string, unknown>> | undefined;
if (opts.resume) {
// Try to find an existing run to resume
const existingRun = await deps.store.getActiveWorkflowRunByPath(workflow.name);
if (existingRun) {
const hydrated = await hydrateResumableRun(deps, existingRun);
workflowRun = hydrated.preCreatedRun;
priorCompletedNodes = hydrated.priorCompletedNodes;
} else {
// No existing run — create a new one
workflowRun = await createNewRun(deps, workflow, userMessage, opts);
}
} else {
// 4. Path-lock guard: check no other run is active
const activeRun = await deps.store.getActiveWorkflowRunByPath(workflow.name);
if (activeRun) {
const errorMsg = `Workflow "${workflow.name}" already has an active run (${activeRun.id}). Wait for it to complete or cancel it first.`;
await safeSendMessage(platform, conversationId, `${errorMsg}`);
return {
run: createFailedRun(workflow, new Error(errorMsg)),
success: false,
error: errorMsg,
};
}
workflowRun = await createNewRun(deps, workflow, userMessage, opts);
}
// 5. Pre-create artifacts directory
const paths = resolveProjectPaths(deps, cwd, workflowRun.id, opts.codebaseId);
try {
await mkdir(paths.artifactsDir, { recursive: true });
await mkdir(paths.logDir, { recursive: true });
} catch (err) {
// Artifacts dir creation is best-effort
}
// 6. Set status to running
try {
workflowRun = await deps.store.updateWorkflowRun(workflowRun.id, {
status: 'running',
});
} catch (err) {
return {
run: workflowRun,
success: false,
error: `Failed to set workflow run status to running: ${err instanceof Error ? err.message : String(err)}`,
};
}
// 7. Notify user
await safeSendMessage(
platform,
conversationId,
`🚀 Starting workflow "${workflow.name}" (run ${workflowRun.id})`,
);
// 8. Execute the DAG
let dagResult: DagWorkflowResult | undefined;
try {
dagResult = await executeDagWorkflow(
deps,
platform,
conversationId,
cwd,
workflow,
workflowRun,
priorCompletedNodes,
);
// 9. Update run status on completion
if (dagResult.success) {
workflowRun = await deps.store.updateWorkflowRun(workflowRun.id, {
status: 'completed',
output: Object.fromEntries(dagResult.nodeOutputs),
});
await safeSendMessage(
platform,
conversationId,
`✅ Workflow "${workflow.name}" completed successfully\n${dagResult.summary}`,
);
} else {
workflowRun = await deps.store.failWorkflowRun(
workflowRun.id,
dagResult.error ?? 'Workflow failed',
);
await safeSendMessage(
platform,
conversationId,
`❌ Workflow "${workflow.name}" failed: ${dagResult.error}`,
);
}
return {
run: workflowRun,
dagResult,
success: dagResult.success,
error: dagResult.error,
};
} catch (err) {
// Unhandled error — update DB and notify
const errorMsg = err instanceof Error ? err.message : String(err);
try {
workflowRun = await deps.store.failWorkflowRun(workflowRun.id, errorMsg);
} catch {
// Best-effort DB update
}
await safeSendMessage(
platform,
conversationId,
`❌ Workflow "${workflow.name}" failed with error: ${errorMsg}`,
);
// Emit error event
try {
await deps.store.createWorkflowEvent({
runId: workflowRun.id,
type: 'workflow_failed',
data: { error: errorMsg },
});
} catch {
// Best-effort event emission
}
return {
run: workflowRun,
success: false,
error: errorMsg,
};
}
}
// ---------------------------------------------------------------------------
// Resume support
// ---------------------------------------------------------------------------
/**
* Hydrate a resumable workflow run.
*
* Loads completed node outputs from the previous run and sets the
* run status back to 'running' so execution can continue.
*
* @param deps - Dependency injection container.
* @param candidate - The existing workflow run to resume.
* @returns Hydrated run with prior completed nodes.
*/
export async function hydrateResumableRun(
deps: WorkflowDeps,
candidate: WorkflowRun,
): Promise<HydratedResumableRun> {
// Load completed node outputs from the previous run
const priorCompletedNodes = await deps.store.getCompletedDagNodeOutputs(candidate.id);
// Resume the workflow run (set status back to 'running')
const preCreatedRun = await deps.store.resumeWorkflowRun(candidate.id);
return {
preCreatedRun,
priorCompletedNodes,
};
}
// ---------------------------------------------------------------------------
// Project paths
// ---------------------------------------------------------------------------
/**
* Resolve project paths for a workflow run.
*
* Uses codebase-scoped paths if a codebaseId is provided,
* otherwise falls back to cwd-based paths.
*
* @param deps - Dependency injection container.
* @param cwd - Working directory.
* @param workflowRunId - The workflow run id.
* @param codebaseId - Optional codebase id for scoped paths.
* @returns Resolved artifacts and log directories.
*/
export function resolveProjectPaths(
_deps: WorkflowDeps,
cwd: string,
workflowRunId: string,
codebaseId?: string,
): ProjectPaths {
if (codebaseId) {
// Codebase-scoped paths
return {
artifactsDir: resolve(cwd, '.ion', 'codebases', codebaseId, 'artifacts', workflowRunId),
logDir: resolve(cwd, '.ion', 'codebases', codebaseId, 'logs', workflowRunId),
};
}
// Cwd-based paths (default)
return {
artifactsDir: resolve(cwd, '.ion', 'artifacts', workflowRunId),
logDir: resolve(cwd, '.ion', 'logs', workflowRunId),
};
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
* Create a new workflow run in the store.
*/
async function createNewRun(
deps: WorkflowDeps,
workflow: WorkflowDefinition,
userMessage: string,
opts: WorkflowExecutionOptions,
): Promise<WorkflowRun> {
const data: CreateWorkflowRunData = {
workflowPath: workflow.name,
workflowName: workflow.name,
trigger: 'manual',
input: {
message: userMessage,
...(opts.input ?? {}),
},
};
return deps.store.createWorkflowRun(data);
}
/**
* Create a minimal failed run object for error cases where
* the store is not available.
*/
function createFailedRun(workflow: WorkflowDefinition, error: unknown): WorkflowRun {
return {
id: 'error',
workflowPath: workflow.name,
workflowName: workflow.name,
status: 'failed',
trigger: 'manual',
input: {},
error: error instanceof Error ? error.message : String(error),
createdAt: new Date(),
updatedAt: new Date(),
};
}