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

122 lines (121 loc) • 5.62 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 { ConfigError } from "../../core/errors.js"; import { assertFrozenDirectoryContained } from "../../execution/directory-identity.js"; import { canonicalResolvedExecutionRequest } from "../../execution/resolved-request.js"; import { buildExecutionFromWire } from "../../integrations/agent/execution.js"; import { runExecution, unwrapHarnessReply } from "../../integrations/agent/runner-dispatch.js"; /** Lower a frozen common request through its frozen runner only. */ export function prepareWorkflowExecution(request, prompt = request.prompt) { const target = request.frozenTarget; if (target.cwdIdentity) assertFrozenDirectoryContained(target.cwdIdentity); const wire = JSON.parse(canonicalResolvedExecutionRequest(target.request)); const command = { ...wire.command, content: prompt }; const runtime = { ...wire.runtime, ...(request.cwd !== undefined ? { workspace: request.cwd } : target.cwdIdentity ? { workspace: target.cwdIdentity.realCwd } : {}), ...(request.env !== undefined ? { environment: request.env } : {}), }; wire.command = command; wire.runtime = runtime; if (request.systemPrompt !== undefined) { wire.conversation = [{ role: "system", content: request.systemPrompt }]; } return buildExecutionFromWire({ request: wire, runner: target.runner }); } function message(err) { return err instanceof Error ? err.message : String(err); } /** * The `eventSource` to forward into `runExecution`, or undefined when the * unit's own `env:` already binds `AKM_EVENT_SOURCE` — an authored binding * wins, as it does for exec units. */ export function forwardedDispatchEventSource(request) { if (request.eventSource === undefined) return undefined; if (request.env?.AKM_EVENT_SOURCE !== undefined) return undefined; return request.eventSource; } /** * Dispatch one frozen workflow engine call through the common prepared/lowered * seam. Credential materialization happens inside the final dispatch only. */ export async function dispatchWorkflowExecution(request, feedback) { const prompt = feedback ? `${request.prompt}\n\n${feedback}` : request.prompt; // Internal invariant: native-executor routes child-workflow units to the // child executor before dispatch, so reaching here is an engine bug. if (request.frozenTarget.kind === "child-workflow") { throw new Error(`unit ${JSON.stringify(request.unitId)} targets child workflow ${JSON.stringify(request.frozenTarget.ref)}, ` + "but reached dispatchWorkflowExecution directly. The child-workflow dispatch seam " + "(src/workflows/exec/child-workflow.ts) should have routed it before dispatch was ever reached — " + "this is an engine routing bug, not a problem with the workflow itself."); } if (request.frozenTarget.kind !== "command") { throw new ConfigError(`unit ${JSON.stringify(request.unitId)} is not a command target.`, "INVALID_CONFIG_FILE"); } const lowered = prepareWorkflowExecution(request, prompt); const notices = lowered.notices.length > 0 ? lowered.notices : undefined; if (request.env && Object.keys(request.env).length > 0 && lowered.runner.kind === "llm") { return { ok: false, text: "", failureReason: "env_unsupported", error: `unit ${JSON.stringify(request.unitId)} declares env bindings, which require a child process ` + `(agent or sdk runner) — the "llm" runner cannot inject a per-unit child environment.`, ...(notices ? { notices } : {}), }; } if (request.cwd && lowered.runner.kind === "llm") { return { ok: false, text: "", failureReason: "isolation_unsupported", error: `unit ${JSON.stringify(request.unitId)} declares isolation: worktree but resolved to the "llm" runner, ` + `which has no working directory to isolate. Use the agent or sdk runner for isolated units.`, ...(notices ? { notices } : {}), }; } let result; try { const eventSource = forwardedDispatchEventSource(request); result = await runExecution(lowered, { runOptions: { stdio: "captured", parseOutput: "text", ...(request.signal ? { signal: request.signal } : {}), }, ...(eventSource !== undefined ? { eventSource } : {}), }); } catch (err) { if (err instanceof ConfigError) throw err; return { ok: false, text: "", failureReason: "dispatch_error", error: message(err), ...(notices ? { notices } : {}), }; } const extraction = result.ok ? unwrapHarnessReply(lowered.runner, result) : undefined; const text = extraction?.text ?? result.stdout; const sessionId = extraction?.sessionId ?? result.sessionId; return { ok: result.ok, text, ...(sessionId !== undefined ? { sessionId } : {}), ...(result.reason ? { failureReason: result.reason } : {}), ...(result.error ? { error: result.error } : {}), ...(result.usage ? { usage: result.usage } : {}), ...(notices ? { notices } : {}), }; }