automagik-genie
Version:
Self-evolving AI agent orchestration framework with Model Context Protocol support
190 lines (189 loc) • 7.53 kB
JavaScript
"use strict";
/**
* Task Command - Pure headless task execution
*
* Creates a Forge task and returns immediately with task ID.
* No browser opening, no monitoring. Designed for automation and scripting.
*
* Behavior:
* 1. Create Forge task with agent
* 2. Return task ID and URL immediately
* 3. Task runs in background
* 4. Exit (fire and forget)
*
* This preserves the old `genie run` behavior for backward compatibility.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.runTask = runTask;
const service_config_js_1 = require("../lib/service-config.js");
const headless_helpers_1 = require("../lib/headless-helpers");
const agent_resolver_1 = require("../lib/agent-resolver");
const forge_executor_1 = require("../lib/forge-executor");
const forge_helpers_1 = require("../lib/forge-helpers");
const executor_registry_1 = require("../lib/executor-registry");
const task_monitor_1 = require("../lib/task-monitor");
const task_service_js_1 = require("../cli-core/task-service.js");
async function runTask(parsed, config, paths) {
const [subcommandOrAgent, ...restArgs] = parsed.commandArgs;
if (!subcommandOrAgent) {
console.error('Usage: genie task <agent> "<prompt>" OR genie task monitor <attempt-id>');
process.exit(1);
}
if (subcommandOrAgent === 'monitor') {
return runTaskMonitor(restArgs, config, paths);
}
const agentName = subcommandOrAgent;
const promptParts = restArgs;
const prompt = promptParts.join(' ').trim();
if (!prompt) {
console.error('Error: Prompt is required');
console.error('Usage: genie task <agent> "<prompt>"');
process.exit(1);
}
const resolvedAgentName = (0, agent_resolver_1.resolveAgentIdentifier)(agentName);
const agentSpec = (0, agent_resolver_1.loadAgentSpec)(resolvedAgentName);
const agentGenie = agentSpec.meta?.genie || {};
const agentMeta = agentSpec.meta || {};
// Resolve executor configuration (CLI flags override agent/config defaults)
const executorKey = (0, executor_registry_1.normalizeExecutorKeyOrDefault)(parsed.options.executor || (0, executor_registry_1.normalizeExecutorValue)(agentGenie.executor) || config.defaults?.executor);
// Derive executor variant matching Forge's naming convention
const deriveVariantFromAgentName = (agentPath) => {
// Forge variant naming: CODE_<AGENT_NAME> or CREATE_<AGENT_NAME>
// Examples: code/explore → CODE_EXPLORE, create/writer → CREATE_WRITER
const parts = agentPath.split('/');
const template = parts[0]; // code, create, etc.
// Remove template and category folders (agents/, workflows/)
let remaining = parts.slice(1);
if (remaining.length > 0 && (remaining[0] === 'agents' || remaining[0] === 'workflows')) {
remaining = remaining.slice(1);
}
// Join remaining parts with underscores and uppercase
const agentName = remaining.join('_').toUpperCase();
// Prepend template prefix (CODE_, CREATE_, etc.)
const templatePrefix = template.toUpperCase() + '_';
return templatePrefix + agentName;
};
const executorVariant = (parsed.options.variant || // CLI flag (highest priority)
agentMeta.forge_profile_name || // Explicit Forge profile name from frontmatter
agentGenie.executorVariant ||
agentGenie.variant ||
deriveVariantFromAgentName(resolvedAgentName) || // Derive from agent name
config.defaults?.executorVariant || // Config defaults (lowest priority)
'DEFAULT' // Ultimate fallback
).trim().toUpperCase();
const model = parsed.options.model || agentGenie.model || config.defaults?.model;
const sessionName = parsed.options.name;
const raw = parsed.options.raw || false;
// Ensure Forge is running (quiet mode)
await (0, headless_helpers_1.ensureForgeRunning)(true);
const forgeExecutor = (0, forge_executor_1.createForgeExecutor)();
let sessionResult;
try {
sessionResult = await forgeExecutor.createTask({
agentName: resolvedAgentName,
prompt,
executorKey,
executorVariant,
executionMode: 'background',
model,
...(sessionName && { name: sessionName })
});
}
catch (error) {
const reason = (0, forge_helpers_1.describeForgeError)(error);
console.error(JSON.stringify({
error: `Failed to create task: ${reason}`,
hint: forge_helpers_1.FORGE_RECOVERY_HINT
}, null, 2));
process.exit(1);
}
const attemptId = sessionResult.attemptId;
const taskUrl = sessionResult.forgeUrl;
const sessionService = new task_service_js_1.TaskService({
paths: {
tasksFile: paths.tasksFile
}
});
const store = sessionService.load();
const now = new Date().toISOString();
store.sessions[attemptId] = {
agent: resolvedAgentName,
taskId: sessionResult.taskId,
projectId: sessionResult.projectId,
executor: executorKey,
executorVariant,
model: model || undefined,
status: 'running',
created: now,
lastUsed: now,
lastPrompt: prompt.slice(0, 200),
mode: 'background',
forgeUrl: sessionResult.forgeUrl,
background: true
};
await sessionService.save(store);
// Output based on --raw flag
if (raw) {
console.log(attemptId);
}
else {
const jsonOutput = {
task_id: attemptId,
task_url: taskUrl,
agent: resolvedAgentName,
executor: `${executorKey}:${executorVariant}`,
...(model && { model }),
status: 'started',
message: 'Task running in background'
};
console.log(JSON.stringify(jsonOutput, null, 2));
}
process.exitCode = 0;
}
async function runTaskMonitor(args, _config, _paths) {
const [attemptId] = args;
if (!attemptId) {
console.error('Usage: genie task monitor <attempt-id>');
process.exit(1);
}
const { baseUrl, token } = (0, service_config_js_1.getForgeConfig)();
console.log(`📡 Monitoring task attempt: ${attemptId}`);
console.log('');
try {
const result = await (0, task_monitor_1.monitorTaskCompletion)({
attemptId,
baseUrl,
token,
onLog: (log) => {
console.log(log);
},
onStatus: (status) => {
if (status !== 'running') {
console.log(`Status: ${status}`);
}
}
});
const jsonOutput = JSON.stringify({
task_url: result.task_url,
result: result.output,
status: result.status,
duration_ms: result.duration_ms,
attempt_id: attemptId,
...(result.error && { error: result.error })
}, null, 2);
console.log('');
console.log('━'.repeat(60));
console.log(result.status === 'completed' ? '✅ Task Completed' : '❌ Task Failed');
console.log('━'.repeat(60));
console.log('');
console.log(jsonOutput);
console.log('');
process.exitCode = result.status === 'completed' ? 0 : 1;
}
catch (error) {
console.error('');
console.error('❌ Monitoring failed:', error);
console.error('');
process.exitCode = 1;
}
}