automagik-genie
Version:
Self-evolving AI agent orchestration framework with Model Context Protocol support
164 lines (163 loc) • 6.46 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports._internals = void 0;
exports.loadTasks = loadTasks;
exports.saveTasks = saveTasks;
const fs_1 = __importDefault(require("fs"));
const executor_registry_1 = require("./lib/executor-registry");
function loadTasks(paths = {}, config = {}, defaults = {}, callbacks = {}) {
const storePath = paths.tasksFile;
let store;
if (storePath && fs_1.default.existsSync(storePath)) {
store = normalizeTaskStore(readJson(storePath, callbacks), callbacks);
}
else {
store = { version: 4, sessions: {} };
}
const defaultExecutor = resolveDefaultExecutor(config, defaults);
return migrateTaskEntries(store, defaultExecutor);
}
function saveTasks(paths = {}, store) {
const target = paths.tasksFile;
if (!target)
return;
const payload = JSON.stringify(store, null, 2);
fs_1.default.writeFileSync(target, payload);
}
function readJson(filePath, callbacks) {
const content = fs_1.default.readFileSync(filePath, 'utf8');
if (!content.trim().length)
return {};
try {
return JSON.parse(content);
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
callbacks.onWarning?.(`Could not parse JSON from ${filePath}: ${message}`);
return {};
}
}
function normalizeTaskStore(data, callbacks = {}) {
if (!data || typeof data !== 'object') {
return { version: 4, sessions: {} };
}
const incoming = data;
// Version 4 format (sessions keyed by attemptId/UUID) - current
if (incoming.version === 4 && incoming.sessions) {
return {
version: 4,
sessions: incoming.sessions
};
}
// Version 3 or earlier - MIGRATE to v4
// Rationale: Preserve user's ability to resume/stop/view existing tasks
// Key change: Use attemptId (from v3's sessionId field) as the v4 key
if (incoming.version && incoming.version < 4) {
const sessions = {};
let migratedCount = 0;
// v3 sessions were keyed by friendly name, need to rekey by attemptId
Object.entries(incoming.sessions || {}).forEach(([_friendlyName, entry]) => {
if (!entry || typeof entry !== 'object')
return;
// v3 stored attemptId in the 'sessionId' field
const attemptId = entry.sessionId;
if (!attemptId || typeof attemptId !== 'string')
return;
// Migrate to v4 format (taskId/projectId will be undefined for migrated sessions)
sessions[attemptId] = {
agent: entry.agent,
// taskId and projectId omitted (not available in v3)
preset: entry.preset,
mode: entry.mode,
executor: entry.executor,
executorVariant: entry.executorVariant,
model: entry.model,
status: entry.status,
created: entry.created,
lastUsed: entry.lastUsed,
lastPrompt: entry.lastPrompt,
forgeUrl: entry.forgeUrl,
background: entry.background
};
migratedCount++;
});
callbacks.onWarning?.(`Migrated ${migratedCount} task(s) from v${incoming.version} to v4. ` +
`Tasks can be viewed/resumed/stopped using their IDs. ` +
`Some metadata (taskId/projectId) may be incomplete for migrated tasks.`);
return {
version: 4,
sessions
};
}
// No version number - treat as v2 and migrate
if (!incoming.version && incoming.sessions) {
const sessions = {};
let migratedCount = 0;
// v2 sessions were keyed by taskId (attemptId), already in correct format
Object.entries(incoming.sessions).forEach(([attemptId, entry]) => {
if (!entry || typeof entry !== 'object')
return;
// v2 already used attemptId as key, just need to copy fields
sessions[attemptId] = {
agent: entry.agent,
// taskId and projectId omitted (not available in v2)
preset: entry.preset,
mode: entry.mode,
executor: entry.executor,
executorVariant: entry.executorVariant,
model: entry.model,
status: entry.status,
created: entry.created,
lastUsed: entry.lastUsed,
lastPrompt: entry.lastPrompt,
forgeUrl: entry.forgeUrl,
background: entry.background
};
migratedCount++;
});
callbacks.onWarning?.(`Migrated ${migratedCount} task(s) from v2 to v4. ` +
`Tasks can be viewed/resumed/stopped using their IDs. ` +
`Some metadata (taskId/projectId) may be incomplete for migrated tasks.`);
return {
version: 4,
sessions
};
}
// Empty or malformed
return {
version: 4,
sessions: {}
};
}
function migrateTaskEntries(store, defaultExecutor) {
const result = {
version: store.version ?? 4,
sessions: { ...store.sessions }
};
// Migrate session entries (apply defaults)
Object.entries(result.sessions || {}).forEach(([attemptId, entry]) => {
if (!entry || typeof entry !== 'object')
return;
if (!entry.mode && entry.preset)
result.sessions[attemptId].mode = entry.preset;
if (!entry.preset && entry.mode)
result.sessions[attemptId].preset = entry.mode;
const normalized = entry.executor ? (0, executor_registry_1.normalizeExecutorKey)(entry.executor) : undefined;
result.sessions[attemptId].executor = normalized ?? defaultExecutor;
});
return result;
}
function resolveDefaultExecutor(config = {}, defaults = {}) {
return (0, executor_registry_1.normalizeExecutorKeyOrDefault)(config.defaults?.executor ??
defaults.defaults?.executor ??
executor_registry_1.DEFAULT_EXECUTOR_KEY, executor_registry_1.DEFAULT_EXECUTOR_KEY);
}
exports._internals = {
readJson,
normalizeTaskStore,
migrateTaskEntries,
resolveDefaultExecutor
};