UNPKG

akm-cli

Version:

akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur

99 lines (98 loc) • 4.57 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. /** * Persist an `akm improve` result as one `improve_runs` row in state.db (since * 0.8.0 stdout stays empty unless `--json-to-stdout`). A run that did not * complete still gets a row. */ import crypto from "node:crypto"; import { decodeImproveResult } from "../../core/improve-result.js"; import { redactSensitiveValue } from "../../core/redaction.js"; import { withImmediateTransaction, withStateDb } from "../../core/state-db.js"; import { recordImproveRun } from "../../storage/repositories/improve-runs-repository.js"; /** `<iso-8601 with dashes>-<8 hex>`, e.g. `2026-05-19T17-30-22-123Z-a1b2c3d4` (the suffix breaks same-ms ties). */ export function buildImproveRunId(now = new Date()) { const iso = now.toISOString().replace(/[:.]/g, "-"); const rand = crypto.randomBytes(4).toString("hex"); return `${iso}-${rand}`; } /** Record a finished run (the full result, redacted; dry runs stay filterable). */ export function recordImproveRunResult(stashDir, runId, result, startedAt, sensitiveValues = []) { const decoded = decodeImproveResult(result); const persistedResult = redactSensitiveValue(result, sensitiveValues); withStateDb((db) => { const completedAt = new Date().toISOString(); // Without a launch timestamp, the one embedded in the run id. const resolvedStartedAt = startedAt ?? runId.slice(0, 24).replace(/^(\d{4}-\d{2}-\d{2}T)(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/, "$1$2:$3:$4.$5Z"); // BEGIN IMMEDIATE with retry: a bare write surfaced "database is locked" (#948). withImmediateTransaction(db, () => { recordImproveRun(db, { id: runId, startedAt: resolvedStartedAt, completedAt, stashDir, dryRun: Boolean(result.dryRun), strategy: redactSensitiveValue(decoded.strategy, sensitiveValues), scopeMode: result.scope?.mode ?? "all", scopeValue: persistedResult.scope?.value ?? null, guidance: persistedResult.guidance ?? null, ok: Boolean(result.ok), result: persistedResult, }); }); }); } /** * Record a run that did not complete (a signal, e.g. a cron timeout, or an * exception) so it does not vanish from `akm health`: `ok: false` with the * reason. The in-flight actions are gone by then, so the envelope is minimal. */ export function recordTerminatedImproveRun(stashDir, runId, startedAt, reason, ctx) { const completedAt = new Date().toISOString(); const persistedReason = redactSensitiveValue(reason, ctx.sensitiveValues ?? []); const persistedScopeValue = redactSensitiveValue(ctx.scopeValue, ctx.sensitiveValues ?? []); const persistedStrategy = redactSensitiveValue(ctx.strategy, ctx.sensitiveValues ?? []); const minimalResult = redactSensitiveValue({ schemaVersion: 2, ok: false, strategy: persistedStrategy, scope: { mode: ctx.scopeMode ?? "all", ...(persistedScopeValue ? { value: persistedScopeValue } : {}) }, dryRun: Boolean(ctx.dryRun), memorySummary: { eligible: 0, derived: 0 }, actions: [], plannedRefs: [], terminated: { reason: persistedReason, at: completedAt, ...(ctx.errorMessage ? { errorMessage: ctx.errorMessage } : {}), }, }, ctx.sensitiveValues ?? []); withStateDb((db) => { withImmediateTransaction(db, () => { recordImproveRun(db, { id: runId, startedAt, completedAt, stashDir, dryRun: Boolean(ctx.dryRun), strategy: persistedStrategy, scopeMode: ctx.scopeMode ?? "all", scopeValue: persistedScopeValue ?? null, guidance: null, ok: false, result: minimalResult, metadata: { terminated: { reason: persistedReason, at: completedAt, ...(ctx.errorMessage ? { errorMessage: redactSensitiveValue(ctx.errorMessage, ctx.sensitiveValues ?? []) } : {}), }, }, }); }); }); }