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
292 lines (291 loc) • 12.4 kB
JavaScript
// 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 { UsageError } from "../../core/errors.js";
import { createInlineResolvedCommand, createResolvedCommand, createResolvedPersona, } from "../../execution/resolved-request.js";
import { isPortableExecutionAgentSelector, } from "../../execution/source.js";
import { recordIndexedShowUsage } from "../../indexer/usage/show-usage.js";
import { resolveUsageEventSource } from "../../indexer/usage/usage-events.js";
import { fallbackAnnouncement } from "../../integrations/agent/engine-fallback.js";
import { buildExecution, resolveExecution, } from "../../integrations/agent/execution.js";
import { runExecution } from "../../integrations/agent/runner-dispatch.js";
import { parseBuiltinCommandAction } from "./builtin-action.js";
import { loadAdapterExecutionSource, } from "./execution-source-loader.js";
import { applyPortableCommandArguments } from "./portable-template.js";
/**
* The ambient `AKM_EVENT_SOURCE` wins over the task runner's provenance; it
* rides `runExecution`'s single-purpose `eventSource`, never `runOptions.env`,
* so a caller cannot replace the request's own environment.
*/
function runOptionsFor(options) {
const { eventSource, ...rest } = options;
if (eventSource === undefined)
return rest;
return { ...rest, eventSource: process.env.AKM_EVENT_SOURCE ?? eventSource };
}
function own(value, key) {
return Object.hasOwn(value, key);
}
function selectAgent(command, invocationDefaults, current) {
let selected = {
present: false,
source: "command",
};
for (const [source, values] of [
["command", command],
["invocation", invocationDefaults],
["current", current],
]) {
if (values && own(values, "agent"))
selected = { present: true, value: values.agent, source };
}
return selected;
}
function defaultSourceLoader(ref, kind, options) {
return kind === "command"
? loadAdapterExecutionSource(ref, "command", options)
: loadAdapterExecutionSource(ref, "persona", options);
}
function qualifyCommandSelectedPersona(selector, command) {
if (!command || selector.includes("//"))
return selector;
return `${command.identity.bundle}//${selector}`;
}
export async function prepareCommandInvocation(options) {
const action = parseBuiltinCommandAction(options.action);
const sourceLoader = options.sourceLoader ?? defaultSourceLoader;
let renderedCommand;
let commandDefaults = Object.freeze({});
let command;
if (action.kind === "stored") {
const rendered = await sourceLoader(action.ref, "command", {
config: options.config,
...(options.sourceLookup ? { lookup: options.sourceLookup } : {}),
});
if (rendered.kind !== "command")
throw new TypeError("command source loader returned a non-command source");
renderedCommand = rendered;
commandDefaults = rendered.defaults;
const applied = applyPortableCommandArguments(rendered.content, action.arguments, rendered.identity.ref);
command = createResolvedCommand({
source: rendered,
...(own(applied, "argumentInput") ? { argumentInput: applied.argumentInput } : {}),
content: applied.content,
});
}
else {
if (options.inlineContentMode === "literal") {
if (action.arguments !== undefined) {
throw new UsageError("Literal inline command content cannot declare portable arguments.", "INVALID_FLAG_VALUE");
}
command = createInlineResolvedCommand({ template: action.content, content: action.content });
}
else {
const applied = applyPortableCommandArguments(action.content, action.arguments, "inline command");
command = createInlineResolvedCommand({
template: applied.template,
...(own(applied, "argumentInput") ? { argumentInput: applied.argumentInput } : {}),
content: applied.content,
});
}
}
const selectedAgent = selectAgent(commandDefaults, options.invocationDefaults, options.current);
let renderedPersona;
if (selectedAgent.present &&
typeof selectedAgent.value === "string" &&
isPortableExecutionAgentSelector(selectedAgent.value)) {
const lookupRef = selectedAgent.source === "command"
? qualifyCommandSelectedPersona(selectedAgent.value, renderedCommand)
: selectedAgent.value;
const rendered = await sourceLoader(lookupRef, "persona", {
config: options.config,
...(options.sourceLookup ? { lookup: options.sourceLookup } : {}),
});
if (rendered.kind !== "persona")
throw new TypeError("persona source loader returned a non-persona source");
renderedPersona = rendered;
}
const persona = renderedPersona ? createResolvedPersona(renderedPersona) : selectedAgent.present ? null : undefined;
return resolveExecution({
command,
config: options.config,
...(persona !== undefined ? { persona } : {}),
...(renderedPersona ? { agentLayer: { id: renderedPersona.identity.ref, values: renderedPersona.defaults } } : {}),
commandLayer: {
id: renderedCommand?.identity.ref ?? "inline-command",
values: commandDefaults,
},
...(options.invocationDefaults ? { invocationDefaults: options.invocationDefaults } : {}),
...(options.current ? { current: options.current } : {}),
...(options.modelMap ? { modelMap: options.modelMap } : {}),
});
}
function compareText(left, right) {
return left < right ? -1 : left > right ? 1 : 0;
}
const DIAGNOSTIC_PROVENANCE_FIELDS = new Set([
"/inference",
"agent",
"authorization",
"command",
"engine",
"engine.requested",
"model",
"outputSchema",
"persona",
"runtime.environment",
"runtime.settings",
"runtime.timeoutMs",
"runtime.workspace",
"tools",
]);
const DIAGNOSTIC_NOTICE_FIELDS = new Set([
"agent",
"command.content",
"command.extensions",
"conversation",
"engine",
"engine.extensions",
"extensions",
"inference",
"model",
"outputSchema",
"persona",
"persona.extensions",
"runtime.environment",
"runtime.extensions",
"runtime.settings",
"runtime.timeoutMs",
"runtime.workspace",
"tools",
]);
function canonicalDiagnosticProvenanceField(field) {
if (field.startsWith("/inference/"))
return "/inference/*";
return DIAGNOSTIC_PROVENANCE_FIELDS.has(field) ? field : undefined;
}
function canonicalDiagnosticNoticeField(field) {
if (typeof field !== "string")
return undefined;
if (field.startsWith("inference."))
return "inference.*";
return DIAGNOSTIC_NOTICE_FIELDS.has(field) ? field : undefined;
}
function diagnosticProvenance(provenance) {
const canonical = new Map();
for (const [field, source] of Object.entries(provenance)) {
const safeField = canonicalDiagnosticProvenanceField(field);
if (!safeField)
continue;
const candidate = Object.freeze({ field: safeField, layer: source.layer, kind: source.kind, via: source.via });
const previous = canonical.get(safeField);
if (!previous || compareText(JSON.stringify(candidate), JSON.stringify(previous)) < 0) {
canonical.set(safeField, candidate);
}
}
return Object.freeze([...canonical.values()].sort((left, right) => compareText(left.field, right.field)));
}
function safeDiagnosticToken(value) {
return value.length <= 128 && /^[a-z][a-z0-9-]*$/.test(value);
}
/** Rebuild a known lowerer notice instead of forwarding its optional details or arbitrary message bytes. */
function safeDiagnosticNotice(notice) {
if (!safeDiagnosticToken(notice.adapter))
return undefined;
switch (notice.code) {
case "untranslated-field": {
const field = canonicalDiagnosticNoticeField(notice.field);
if (!field)
return undefined;
return Object.freeze({
code: "untranslated-field",
severity: "warning",
adapter: notice.adapter,
field,
message: `The ${notice.adapter} lowerer does not translate resolved field ${field}; dispatch will continue optimistically.`,
});
}
case "conversation-prompt-composed":
return Object.freeze({
code: "conversation-prompt-composed",
severity: "warning",
adapter: notice.adapter,
field: "conversation",
message: `The ${notice.adapter} transport has no native multi-message channel; AKM composed the conversation prefix into one deterministic JSON prompt block.`,
});
case "persona-prompt-composed":
return Object.freeze({
code: "persona-prompt-composed",
severity: "info",
adapter: notice.adapter,
field: "persona",
message: "The selected engine has no native persona channel; AKM composed the persona into the prompt.",
});
default:
return undefined;
}
}
function diagnosticNotices(notices) {
const canonical = new Map();
for (const notice of notices) {
const safe = safeDiagnosticNotice(notice);
if (safe)
canonical.set(JSON.stringify(safe), safe);
}
return Object.freeze([...canonical.values()].sort((left, right) => compareText(JSON.stringify(left), JSON.stringify(right))));
}
/**
* Build a prepared invocation and project only safe structural diagnostics.
* Reads no credential, records no usage, and exposes no request values.
*/
export function inspectPreparedCommandInvocation(prepared) {
const built = buildExecution(prepared.request, prepared.runner);
return {
schemaVersion: 1,
shape: "command-dry-run",
ok: true,
dryRun: true,
engine: prepared.request.engine.name,
provenance: diagnosticProvenance(prepared.provenance),
notices: diagnosticNotices(built.notices),
};
}
function resultEnvelope(result, engine, warnings, notices) {
return {
schemaVersion: 2,
ok: result.ok,
shape: "agent-result",
engine,
exitCode: result.exitCode,
stdout: result.stdout ?? "",
stderr: result.stderr ?? "",
durationMs: result.durationMs,
...(result.error !== undefined ? { error: result.error } : {}),
...(result.reason !== undefined ? { reason: result.reason } : {}),
...(warnings.length > 0 ? { warnings } : {}),
...(notices.length > 0 ? { notices } : {}),
};
}
export async function dispatchPreparedCommandInvocation(prepared, options = {}) {
const { request } = prepared;
const built = buildExecution(request, prepared.runner);
const selectedEngine = request.engine.name;
const result = await runExecution(built, runOptionsFor(options));
const consumedRefs = new Set();
if (request.command.source)
consumedRefs.add(request.command.source.ref);
if (request.persona)
consumedRefs.add(request.persona.source.ref);
// F-1 (spec §5.2 point 3): options.eventSource is only a FALLBACK — an
// ambient AKM_EVENT_SOURCE still wins (D5 clause d). Absent, this is
// byte-identical to the pre-P1b bare resolveUsageEventSource() call (P-07's
// own default is "user").
const eventSource = resolveUsageEventSource(process.env, options.eventSource ?? "user");
for (const ref of consumedRefs)
recordIndexedShowUsage(ref, eventSource);
const announcement = fallbackAnnouncement(prepared.fallbackEngineName, selectedEngine);
return resultEnvelope(result, selectedEngine, announcement ? [announcement] : [], built.notices);
}
export async function executeCommandInvocation(options, dispatchOptions = {}) {
return dispatchPreparedCommandInvocation(await prepareCommandInvocation(options), dispatchOptions);
}