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

128 lines (127 loc) • 5.87 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/. import { eligibleRefCount, MODEL_CALLING_PROCESSES, projectResolvedProcessRouting, } from "./improve-strategies.js"; /** The autonomy lane gating each model-calling process, if any. */ const AUTONOMY_LANE_BY_PROCESS = { memoryInference: "memoryInference", }; function dominantReason(counts) { let best; let bestCount = 0; for (const [reason, count] of Object.entries(counts)) { if (count > bestCount) { best = reason; bestCount = count; } } return best; } /** * Why a model-calling process made no call, in priority order: its engine is * unavailable; disabled — `autonomy_gated` when its lane was gated, else * nothing to report; every ref strategy-filtered; its dominant skip reason; * else `no_signal`. The reasons are the ones the events and results already * use. */ export function deriveNoCallReason(args) { const { row } = args; if (row.unavailable) return "engine_unavailable"; const lane = AUTONOMY_LANE_BY_PROCESS[row.process]; const laneGated = lane !== undefined && args.autonomyGated.some((gated) => gated.lane === lane); if (!row.enabled) return laneGated ? "autonomy_gated" : undefined; if (args.eligibleRefs === 0 && args.strategyFilteredRefsCount > 0) return "strategy_filtered_all_passes"; const dominant = args.skipReasonCounts ? dominantReason(args.skipReasonCounts) : undefined; if (dominant) return dominant; return "no_signal"; } /** Reflect's skip reasons, from its skipped (and older cooldown) actions. */ function countReflectSkipReasons(actions) { const counts = {}; for (const action of actions) { if (action.mode !== "reflect-cooldown" && action.mode !== "reflect-skipped") continue; const result = action.result; const reason = typeof result?.reason === "string" && result.reason.trim() ? result.reason : "unknown"; counts[reason] = (counts[reason] ?? 0) + 1; } return counts; } /** This run's `usageReport`, or `undefined` when both halves are empty. */ export function buildImproveUsageReport(args) { // Only the processes that call a model themselves, on any engine kind: triage // (attributed to its judgment) and proactive maintenance (no engine) would // always read as "zero calls". The "triage.judgment" row is dropped too: its // calls are never attributed to it (#947). const routing = projectResolvedProcessRouting(args.resolvedPlan).filter((row) => row.process !== "triage.judgment" && MODEL_CALLING_PROCESSES.has(row.process)); const calledProcesses = new Set(args.byProcessEngineModel.filter((row) => row.calls > 0).map((row) => row.process)); const reflectSkipCounts = countReflectSkipReasons(args.persistedActions); const noCalls = []; for (const row of routing) { if (calledProcesses.has(row.process)) continue; const eligibleRefs = eligibleRefCount(args.loopRefs, row.process, args.resolvedPlan.strategy.config); const reason = deriveNoCallReason({ row, autonomyGated: args.resolvedPlan.autonomyGated, strategyFilteredRefsCount: args.strategyFilteredRefsCount, eligibleRefs, skipReasonCounts: row.process === "reflect" ? reflectSkipCounts : row.process === "distill" ? args.distillSkippedAggregate?.byReason : undefined, }); if (reason === undefined) continue; noCalls.push({ process: row.process, ...(row.engine ? { engine: row.engine } : {}), reason }); } if (args.byProcessEngineModel.length === 0 && noCalls.length === 0) return undefined; return { byProcessEngineModel: args.byProcessEngineModel, noCalls }; } function renderFixedWidthTable(headers, rows) { const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0))); const renderRow = (cells) => cells .map((cell, index) => cell.padEnd(widths[index] ?? 0)) .join(" ") .trimEnd(); return [renderRow(headers), ...rows.map(renderRow)]; } /** The usage report as a fixed-width table; `notes` carry precision caveats (e.g. an older run recomputed from events). */ export function formatUsageReportTable(usageReport, notes) { const lines = ["[improve] usage report (process x engine x model):"]; if (usageReport.byProcessEngineModel.length === 0) { lines.push(" (no LLM calls recorded)"); } else { const headers = ["process", "engine", "model", "calls", "failures", "promptTok", "complTok", "totalTok", "ms"]; const rows = usageReport.byProcessEngineModel.map((row) => [ row.process, row.engine, row.model, String(row.calls), String(row.failures), String(row.promptTokens), String(row.completionTokens), String(row.totalTokens), String(row.totalDurationMs), ]); for (const line of renderFixedWidthTable(headers, rows)) lines.push(` ${line}`); } if (usageReport.noCalls.length > 0) { lines.push("[improve] enabled processes with zero calls:"); const headers = ["process", "engine", "reason"]; const rows = usageReport.noCalls.map((row) => [row.process, row.engine ?? "-", row.reason]); for (const line of renderFixedWidthTable(headers, rows)) lines.push(` ${line}`); } for (const note of notes ?? []) lines.push(` note: ${note}`); return lines.join("\n"); }