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

467 lines (466 loc) • 19.2 kB
{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://itlackey.github.io/akm/schemas/akm-workflow.json", "title": "AKM Workflow", "description": "This frontmatter schema applies to and validates the Markdown (.md) workflow source: the standard AKM asset envelope plus its orchestration graph. Markdown is not the sole or only workflow source; peer GitHub-shaped .yml sources use the bounded adapter and both formats compile to source IR v1. Per-step prose instructions and gate rubrics live in the Markdown body, joined to this graph by step id. Hand-authored alongside src/workflows/program/schema.ts; see docs/reference/workflow-schema.md.", "type": "object", "required": ["steps"], "additionalProperties": false, "properties": { "type": { "$ref": "akm-asset-envelope.json#/definitions/type" }, "description": { "$ref": "akm-asset-envelope.json#/definitions/description" }, "tags": { "$ref": "akm-asset-envelope.json#/definitions/tags" }, "when_to_use": { "$ref": "akm-asset-envelope.json#/definitions/when_to_use" }, "xrefs": { "$ref": "akm-asset-envelope.json#/definitions/xrefs" }, "updated": { "$ref": "akm-asset-envelope.json#/definitions/updated" }, "timestamp": { "$ref": "akm-asset-envelope.json#/definitions/timestamp" }, "generated": { "$ref": "akm-asset-envelope.json#/definitions/generated" }, "verified": { "$ref": "akm-asset-envelope.json#/definitions/verified" }, "provenance": { "$ref": "akm-asset-envelope.json#/definitions/provenance" }, "status": { "$ref": "akm-asset-envelope.json#/definitions/status" }, "stale_after": { "$ref": "akm-asset-envelope.json#/definitions/stale_after" }, "params": { "type": "object", "minProperties": 1, "description": "Run parameters: param name -> JSON Schema declaration. Names must be params.<name>-addressable identifiers.", "propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, "additionalProperties": { "$ref": "#/definitions/jsonSchemaObject" } }, "outputs": { "type": "object", "minProperties": 1, "description": "Named, optionally schema-validated projections of step artifacts, exported when the run completes. Names must be steps.<child>.output.<name>-addressable identifiers. Each entry: { from: steps.<id>.output(.<seg>)*, schema?: <bounded JSON Schema> }. Markdown-frontmatter only.", "propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, "additionalProperties": { "$ref": "#/definitions/outputDeclaration" } }, "defaults": { "$ref": "#/definitions/defaults" }, "budget": { "$ref": "#/definitions/budget" }, "steps": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/step" } } }, "definitions": { "identifier": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_-]*$", "description": "Step id: a letter or underscore first, then letters, digits, underscores, or dashes (no dots, no leading digit). This is exactly the steps.<id>.output-addressable grammar; dots are reserved for the .output path separator and the engine's internal <stepId>.gate node id." }, "reference": { "type": "string", "minLength": 1, "description": "A bare reference string: params.<name> or steps.<id>.output(.<ident>|[<int>])*. No ${{ }} delimiters — every position this appears in is a whole-value frontmatter field. Full grammar validation happens at compile time, not here." }, "timeout": { "description": "Duration: \"<n>ms\" | \"<n>s\" | \"<n>m\" | \"none\" (bare integers are milliseconds). Must resolve to at most 2147483647 ms (setTimeout's 32-bit signed ceiling) — the parser enforces the same ceiling on the string form.", "oneOf": [ { "type": "string", "pattern": "^([0-9]+(ms|s|m)?|none)$" }, { "type": "integer", "minimum": 1, "maximum": 2147483647 } ] }, "engineName": { "type": "string", "minLength": 1, "maxLength": 63, "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$", "description": "Named engine: lowercase dash-separated words of letters and digits, starting with a letter. Same grammar the frozen-plan decoder enforces (src/workflows/resource-limits.ts WORKFLOW_ENGINE_NAME_PATTERN)." }, "onError": { "description": "Failure policy: fail the step on the first unit failure (default), or record failures and let the gate decide.", "enum": ["fail", "continue"] }, "llm": { "type": "object", "additionalProperties": false, "properties": { "temperature": { "type": "number" }, "max_tokens": { "type": "integer", "minimum": 1 }, "supports_json_schema": { "type": "boolean" }, "extra_params": { "$ref": "#/definitions/extraParams" }, "context_length": { "type": "integer", "minimum": 1 }, "enable_thinking": { "type": "boolean" }, "reasoning_effort": { "type": "string", "minLength": 1 } } }, "extraParams": { "type": "object", "description": "Provider-specific request fields. Top-level AKM-owned fields are forbidden; credential-shaped keys are forbidden recursively after lowercase/non-alphanumeric normalization.", "propertyNames": { "not": { "enum": ["model", "messages", "temperature", "maxtokens", "responseformat", "stream", "streamoptions", "enablethinking", "reasoningeffort", "chattemplatekwargs", "authorization", "headers", "apikey", "token", "password", "secret", "cookie", "setcookie"] } }, "additionalProperties": { "$ref": "#/definitions/extraParamValue" }, "x-akm-protectedTopLevelNormalizedKeys": ["model", "messages", "temperature", "maxtokens", "responseformat", "stream", "streamoptions", "enablethinking", "reasoningeffort", "chattemplatekwargs"], "x-akm-recursivelyForbiddenNormalizedKeys": ["authorization", "headers", "apikey", "token", "password", "secret", "cookie", "setcookie"] }, "extraParamValue": { "anyOf": [ { "type": ["string", "number", "boolean", "null"] }, { "type": "array", "items": { "$ref": "#/definitions/extraParamValue" } }, { "type": "object", "propertyNames": { "not": { "enum": ["authorization", "headers", "apikey", "token", "password", "secret", "cookie", "setcookie"] } }, "additionalProperties": { "$ref": "#/definitions/extraParamValue" }, "x-akm-recursivelyForbiddenNormalizedKeys": ["authorization", "headers", "apikey", "token", "password", "secret", "cookie", "setcookie"] } ] }, "reducer": { "description": "How a map step folds per-item unit results into the step artifact.", "enum": ["collect", "vote"] }, "isolation": { "description": "Filesystem isolation for file-mutating units.", "enum": ["none", "worktree"] }, "failureReason": { "description": "Persisted AgentFailureReason taxonomy (src/integrations/agent/spawn.ts).", "enum": [ "timeout", "spawn_failed", "non_zero_exit", "parse_error", "cooldown", "llm_rate_limit", "llm_content_filter", "llm_invalid_json", "content_policy_reject", "unsupported_type", "no_change", "quality_rejected", "aborted" ] }, "jsonSchemaObject": { "type": "object", "description": "A JSON Schema declaration. The parser validates it as a schema definition against the runtime's enforced subset (src/core/json-schema.ts checkJsonSchemaDefinition): typo'd type names and keywords outside the subset (e.g. $ref, const, format, patternProperties) are authoring-time errors, not silent no-ops." }, "outputDeclaration": { "type": "object", "description": "One outputs: entry: a validated steps.<id>.output(.<seg>)* reference into a step artifact, plus an optional bounded JSON Schema.", "required": ["from"], "additionalProperties": false, "properties": { "from": { "type": "string", "description": "A steps.<id>.output(.<seg>)* reference into a step artifact. Never params.<name> — an output projects a step artifact, never a param." }, "schema": { "$ref": "#/definitions/jsonSchemaObject" } } }, "retry": { "type": "object", "description": "Bounded retry on transient failures, keyed on the persisted failure_reason taxonomy.", "required": ["max", "on"], "additionalProperties": false, "properties": { "max": { "type": "integer", "minimum": 0, "description": "Maximum retry attempts per unit." }, "on": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/failureReason" } } } }, "budget": { "type": "object", "description": "Run-level budget ceilings, enforced by the engine per run (journal-seeded). Hitting a ceiling fails the step hard, regardless of on_error.", "additionalProperties": false, "properties": { "max_tokens": { "type": "integer", "minimum": 1, "description": "Ceiling on total reported token usage across the run (seeded from journaled unit rows)." }, "max_units": { "type": "integer", "minimum": 1, "description": "Ceiling on total dispatched units across the run (seeded from journaled unit rows)." } } }, "defaults": { "type": "object", "description": "Run-level defaults, overridable per unit.", "additionalProperties": false, "properties": { "engine": { "$ref": "#/definitions/engineName" }, "model": { "type": "string", "minLength": 1, "description": "Model alias (tier) or exact id; resolved per-harness at dispatch." }, "timeout": { "$ref": "#/definitions/timeout" }, "on_error": { "$ref": "#/definitions/onError" }, "llm": { "$ref": "#/definitions/llm" } } }, "exec": { "type": "object", "description": "Run a shell command as this unit instead of dispatching to an engine. `command` is an ARGV ARRAY and there is no shell-string form: the child is spawned directly, so shell metacharacters (`;` `|` `&&` `$(…)` `>` `*`) are inert literal argument bytes — the quoting/injection class simply does not exist. Write the interpreter explicitly (e.g. [\"bash\", \"-lc\", \"a | b\"]) when a pipeline is really wanted. stdout is the promoted step artifact with trailing newlines stripped (like shell `$(…)`); when the unit declares an `output` schema, stdout must instead be exactly one JSON value, which is validated against it. A non-zero exit fails the unit with failure reason `non_zero_exit`, honoring `retry:` and `on_error:` like any other unit. The child's environment is an ALLOWLIST by default (PATH, HOME, locale/temp vars, the Windows process-creation essentials) — widen it only with named `env:` environment bindings or `pass_env`.", "required": ["command"], "additionalProperties": false, "properties": { "command": { "type": "array", "description": "argv; command[0] is the program, resolved through PATH. Never shell-parsed.", "minItems": 1, "items": { "type": "string", "minLength": 1 } }, "cwd": { "type": "string", "description": "Optional RELATIVE working directory inside the unit's working directory (its fresh worktree under `isolation: worktree`). Absolute paths, Windows drive letters, \"~\", and \"..\" segments are rejected here, and containment is re-checked against the resolved base — symlinks included — before the command is spawned.", "minLength": 1, "pattern": "^(?!/|\\\\|~|[A-Za-z]:)(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$" }, "pass_env": { "type": "array", "description": "Extra environment variable NAMES copied through from akm's own environment on top of the default allowlist — for a per-machine toolchain variable (e.g. CARGO_HOME) that no committed `env:` asset could carry. NAMES ONLY: values never appear in a workflow, and unlike `env:` bindings these values are NOT redacted from the command's output, so never list a credential here.", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" } } } }, "unit": { "type": "object", "description": "Optional dispatch-override bag for a unit/map step. A step with none of these carries the run's engine/model/timeout defaults verbatim. Declaring `exec` turns the step into a shell-command unit: it names no engine, so `engine`, `model`, and `llm` are rejected alongside it.", "additionalProperties": false, "allOf": [ { "if": { "required": ["exec"] }, "then": { "not": { "anyOf": [ { "required": ["engine"] }, { "required": ["model"] }, { "required": ["llm"] } ] } } } ], "properties": { "exec": { "$ref": "#/definitions/exec" }, "engine": { "$ref": "#/definitions/engineName", "description": "Named engine selected when the workflow run is created." }, "model": { "type": "string", "minLength": 1 }, "llm": { "$ref": "#/definitions/llm" }, "timeout": { "$ref": "#/definitions/timeout" }, "retry": { "$ref": "#/definitions/retry" }, "on_error": { "$ref": "#/definitions/onError" }, "output": { "$ref": "#/definitions/jsonSchemaObject" }, "env": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 }, "description": "Env asset refs injected into the dispatched unit env. Entries must be unique: the frozen-plan decoder rejects duplicates at run start." }, "isolation": { "$ref": "#/definitions/isolation" } } }, "map": { "type": "object", "description": "Fan the step out over a reference-addressed item list.", "required": ["over"], "additionalProperties": false, "properties": { "over": { "$ref": "#/definitions/reference", "description": "Reference naming the producer of the item list (e.g. steps.discover.output.files)." }, "concurrency": { "type": "integer", "minimum": 1, "maximum": 64, "description": "Max concurrent units for this step. Defaults to workflow.defaultMapConcurrency (4 when unset) — a map step is parallel by default since 0.9.1; write 1 for serial execution. The effective width is the minimum of this, workflow.maxConcurrency, the selected engine's concurrency, and the host CPU cap." }, "reducer": { "$ref": "#/definitions/reducer" }, "unit": { "$ref": "#/definitions/unit" } } }, "routeBranch": { "type": "object", "required": ["match", "step"], "additionalProperties": false, "properties": { "match": { "type": ["string", "number", "boolean"], "minLength": 1, "description": "Branch key. An empty string is rejected: the frozen-plan decoder requires every `when` key to be non-empty." }, "step": { "$ref": "#/definitions/identifier" } } }, "route": { "type": "object", "description": "Route on an explicit reference input to a LATER step. Targets must come after the routing step.", "required": ["input", "when"], "additionalProperties": false, "properties": { "input": { "$ref": "#/definitions/reference", "description": "Reference naming the value to route on." }, "when": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/routeBranch" } }, "default": { "$ref": "#/definitions/identifier" } } }, "gate": { "type": "object", "description": "Optional validation-loop configuration. The rubric itself lives in the body under a `### gate` sub-heading inside the step's section; omitted or empty rubric text skips validation.", "additionalProperties": false, "properties": { "max_loops": { "type": "integer", "minimum": 1, "description": "Evaluator-optimizer loop bound." } } }, "inputs": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/definitions/reference" }, "description": "Prior-step artifacts this unit/map step consumes, as reference strings (sub-paths legal). Attached to the dispatched unit as structured context; never spliced into instructions. Entries must be unique: the frozen-plan decoder rejects duplicates at run start." }, "step": { "type": "object", "description": "One step of the gated spine. A step with no map/route is a unit step (bare `{id: ...}` is the complete minimal declaration). At most one of map/route.", "required": ["id"], "additionalProperties": false, "properties": { "id": { "$ref": "#/definitions/identifier" }, "unit": { "$ref": "#/definitions/unit" }, "map": { "$ref": "#/definitions/map" }, "route": { "$ref": "#/definitions/route" }, "inputs": { "$ref": "#/definitions/inputs" }, "output": { "$ref": "#/definitions/jsonSchemaObject" }, "gate": { "$ref": "#/definitions/gate" } }, "allOf": [ { "not": { "required": ["map", "route"] } }, { "if": { "required": ["route"] }, "then": { "not": { "anyOf": [{ "required": ["unit"] }, { "required": ["map"] }, { "required": ["inputs"] }] } } }, { "if": { "required": ["map"] }, "then": { "not": { "required": ["unit"] } } } ] } } }