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

137 lines (136 loc) • 6.27 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 { cloneExecutionJson, cloneExecutionJsonObject } from "./json.js"; export const EXECUTION_SOURCE_SCHEMA_VERSION = 1; /** Current internal adapter identifiers are lowercase kebab-case registry keys. */ export const EXECUTION_ADAPTER_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/; /** * The model-work tool policy: unattended model work (improve, the judges, index * passes, remember) may read, edit only inside the dispatch's own scratch * working directory, and run `akm search` and `akm show`; the stash stays * read-only to it. A transport grants what it can confine and refuses the policy * at build when it can confine nothing; an LLM has no tools. A request carries it * as `authorization.policy.id`, set only when its caller asks (`modelWork`): no * `tools` value names it, so an asset's own `tools:` cannot. */ export const MODEL_WORK_POLICY_ID = "model-work"; function requireRecord(value, path) { if (value === null || typeof value !== "object" || Array.isArray(value)) { throw new TypeError(`${path} must be an object`); } return value; } export function createAdapterExtensions(first, second, ...rest) { const entries = typeof first === "string" ? [[first, second]] : [first, ...(second === undefined ? [] : [second]), ...rest]; const out = {}; for (const [owner, values] of entries) out[owner] = cloneExecutionJsonObject(values, `extensions.${owner}`); return Object.freeze(out); } export function cloneAdapterExtensions(value, path) { const out = {}; for (const [owner, fields] of Object.entries(requireRecord(value, path))) { out[owner] = cloneExecutionJsonObject(fields, `${path}.${owner}`); } return Object.freeze(out); } /** A selector or layer identifier that may cross the durable execution boundary. */ export function requireStableExecutionSelector(value, path) { if (typeof value !== "string" || value.length === 0 || value.trim() !== value) { throw new TypeError(`${path} must be a non-empty stable identifier`); } return value; } export function isPortableExecutionAgentSelector(value) { return /^(?:[^/]+\/\/)?agents\/[^/]/.test(value); } export function executionPersonaMatchesSelector(selector, personaRef) { return selector.includes("//") ? selector === personaRef : personaRef.endsWith(`//${selector}`); } const IDENTITY_KEYS = ["ref", "bundle", "adapter", "file", "hash"]; function identityFrom(value, path) { const record = requireRecord(value, path); const out = {}; for (const key of IDENTITY_KEYS) { const field = record[key]; if (typeof field !== "string" || field.length === 0) throw new TypeError(`${path}.${key} must be a non-empty string`); out[key] = field; } return Object.freeze(out); } /** Copy and freeze an identity produced by an adapter renderer. */ export function createExecutionSourceIdentity(input) { return identityFrom(input, "execution source identity"); } /** Read a source identity back from a frozen request. */ export function decodeExecutionSourceIdentity(value, path = "execution source identity") { return identityFrom(value, path); } /** Type-check the common frontmatter defaults; unknown keys are ignored. */ export function cloneUnresolvedExecutionDefaults(input, path = "execution source defaults") { const record = requireRecord(input, path); const present = (key) => Object.hasOwn(record, key) && record[key] !== undefined; const out = {}; for (const key of ["agent", "engine", "model", "workspace"]) { if (!present(key)) continue; const value = record[key]; if (value !== null && typeof value !== "string") throw new TypeError(`${path}.${key} must be a string or null`); out[key] = value; } if (present("timeout")) { const timeout = record.timeout; if (timeout !== null && typeof timeout !== "string" && typeof timeout !== "number") { throw new TypeError(`${path}.timeout must be a string, number, or null`); } out.timeout = timeout; } for (const key of ["inference", "outputSchema", "runtime"]) { if (!present(key)) continue; out[key] = record[key] === null ? null : cloneExecutionJsonObject(record[key], `${path}.${key}`); } if (present("environment")) { const environment = record.environment === null ? null : cloneExecutionJsonObject(record.environment, `${path}.environment`); if (environment !== null && Object.values(environment).some((value) => typeof value !== "string")) { throw new TypeError(`${path}.environment values must be strings`); } out.environment = environment; } if (present("tools")) out.tools = cloneToolSelection(record.tools, `${path}.tools`); return Object.freeze(out); } export function cloneToolSelection(value, path = "tools") { if (value === null || typeof value === "string") return value; const cloned = cloneExecutionJson(value, path); if (Array.isArray(cloned)) { if (cloned.some((tool) => typeof tool !== "string")) throw new TypeError(`${path} array values must be strings`); return cloned; } return cloneExecutionJsonObject(cloned, path); } export function createAdapterRenderedExecutionSource(input) { const { kind, content } = input; if (kind !== "command" && kind !== "persona") throw new TypeError("execution source kind is invalid"); if (typeof content !== "string") throw new TypeError("execution source content must be a string"); return Object.freeze({ schemaVersion: EXECUTION_SOURCE_SCHEMA_VERSION, kind, content, defaults: cloneUnresolvedExecutionDefaults(input.defaults ?? {}), identity: createExecutionSourceIdentity(input.identity), ...(input.extensions ? { extensions: cloneAdapterExtensions(input.extensions, "execution source extensions") } : {}), }); }