Skip to content

Releases

AKMON v0.6.0

Choose a tag to compare

@github-actions github-actions released this 08 Nov 17:05

AKMON 0.6.0 follows 0.5.0. Its main change: the installable Python package is
now named akmon, and this is its first release on PyPI, so pip install akmon
and uv tool install akmon work. The command and the Python import keep their
names, so every command below still starts with okg and code still says
import okg. It also removes 8 summary views that nobody read from the console.
There are no new features beyond the rename and no new migration files.

Get it

  • PyPI (new): the package is published to PyPI as the project akmon, so
    pip install akmon==0.6.0 and uv tool install akmon==0.6.0 work. It is
    published after the release, by a separate workflow started by hand that
    uploads only the wheel the release pipeline built, using PyPI trusted
    publishing (PyPI trusts that one workflow, so no upload password or token
    exists anywhere). Until that workflow has run, install from the attached
    wheel. The 0.5.0 notes said that release would reach PyPI as okg; it did
    not, and no okg release will.
  • Python package: the wheel akmon-0.6.0-py3-none-any.whl is attached to
    this release. Install it with uv tool install or pip install, then follow
    the quickstart (docs/start/quickstart.md).
  • Containers and Kubernetes: the Install section at the end lists the
    runtime and PostgreSQL images and the Helm chart, each with its digest.

What's new

  • The package is named akmon. Wheels are now named
    akmon-<version>-py3-none-any.whl and their metadata says Name: akmon.
    okg --version and okg.__version__ read their version from that package.
    Messages that suggest an extra or an added package now name akmon.
  • Release tooling reads each release's own package name. Older releases
    named okg still check, and installed upgrades pick the right wheel name for
    the version they fetch. The PyPI workflow uploads only a wheel whose metadata
    names akmon, so v0.4.0 and v0.5.0 do not go to PyPI.
  • The console drops 8 unread views. The workspace deployment's 8 summary
    views and the two Today-page builders that read them are deleted, so Home and
    Sources list 65 producers instead of 73. /console/view stays. Old view
    records stay in the graph.

Breaking changes

  • The distribution is renamed from okg to akmon. A dependency on okg
    must become a dependency on akmon. Pin akmon>=0.6.0. The command okg and
    the import okg do not change, so scripts and code that use them need no edit.
  • Remove okg before installing akmon. Both packages install the same
    okg command and the same okg Python package, and removing okg later
    would delete files akmon needs.
  • v0.4.0 and v0.5.0 stay on GitHub, under the name okg. Their wheels are
    attached to their releases and are not on PyPI.
  • The route /console/view-diagnostics is removed. A request for console
    metrics that names only retired view: keys now gets an empty answer that
    says so.

Upgrading from 0.5.0

  1. Remove the old package: uv tool uninstall okg or pip uninstall okg.
  2. Install the new one: uv tool install akmon or pip install akmon.
  3. Run okg --version. It prints okg 0.6.0.
  4. There is no new migration file and no setting changes. Run
    okg up <deployment> --plan and okg up <deployment> as usual.

To go back to 0.5.0, uninstall akmon first, then install the okg wheel from
the v0.5.0 release.

Known issues

  • PyPI is empty until the upload workflow runs. pip install akmon==0.6.0
    works only after the separate publishing workflow has been started for this
    release.
  • Everything listed as a known issue in the 0.5.0 notes is unchanged,
    except its PyPI entry: 0.5.0 stays on GitHub.

Install

Everything below was built from the commit this tag names. The tags v0.6.0 (images) and 0.6.0 (chart) are never moved or overwritten; pin a digest to be certain.

Artifact Reference Digest
Runtime image ghcr.io/mitdbg/akmon:v0.6.0 sha256:c4e47d5b1d34228fff0e0a69cad6de88cba151b71f056ab5e1a6466df74955d8
PostgreSQL image ghcr.io/mitdbg/akmon-postgres:v0.6.0 sha256:20e6a145332ccf4df6f207970ac34abec217495e796575618248db099be11d1d
Helm chart oci://ghcr.io/mitdbg/charts/akmon version 0.6.0 sha256:d2540130128009e2c47a9e0dc007c3f22a8389045d2858c1ece5a219b4a00bbd
helm install okg oci://ghcr.io/mitdbg/charts/akmon --version 0.6.0 -f values.yaml

The chart's appVersion is v0.6.0: with images.okg.tag and digest empty it runs ghcr.io/mitdbg/akmon:v0.6.0, and its PostgreSQL image is ghcr.io/mitdbg/akmon-postgres:v0.6.0.

AKMON v0.5.0

Choose a tag to compare

@github-actions github-actions released this 05 Nov 03:10

AKMON 0.5.0 follows 0.4.0. It stops the run logs from filling the database:
a source's large record list is stored once per sync instead of five times, and
old copies are deleted after each accepted sync. It makes the graph reader
easier for AI agents to use: run counts links, adds computed columns and
accepts more call shapes; a refusal shows the one call that fixes it; an empty
page says why it is empty; and stored numbers can be totalled. It speeds up two
slow paths, link rules that match by name and joins in the query interpreter.
It also adds the first tools for replaying benchmark questions against a graph
and testing wording changes on an experiment branch, and publishes the wheel to
PyPI. There are no new migration files; the upgrade adds two columns to the
call log. AKMON's Python package and command
are named okg, so every command below starts with okg.

Get it

  • Python package: the wheel okg-0.5.0-py3-none-any.whl is attached to
    this release. Install it with uv tool install or pip install, then
    follow the quickstart (docs/start/quickstart.md).
  • PyPI (new): the same wheel is also published to PyPI as the project
    okg, so pip install okg==0.5.0 and uv tool install okg==0.5.0 work.
    It is published after the release, by a separate workflow started by hand
    that uploads only the wheel the release pipeline built, using PyPI trusted
    publishing (PyPI trusts that one workflow, so no upload password or token
    exists anywhere). Until that workflow has run, install from the attached
    wheel.
  • Containers and Kubernetes: the Install section at the end lists the
    runtime and PostgreSQL images and the Helm chart, each with its digest. The
    PostgreSQL image is unchanged from 0.4.0.

What's new

Storage

  • A source's record list is stored once per sync. A source keeps a "record
    set": the list of record keys it owns and the graph rows each key produced,
    used to retract rows a record no longer has. Before, a record set up to 1 MB
    was copied into the source's saved position, its approval record, every
    approval event and the sync report (saved twice). Now a set over 64 KB
    (65,536 bytes) is stored once in its own table and the other places hold a
    short reference. On a lab copy of a large deployment, run-log growth per sync
    fell 85% with an identical graph. OKG_SOURCE_RUNNER_INLINE_RECORD_SET_MAX_BYTES
    changes the 64 KB limit. If a stored set goes missing, okg doctor warns
    source_record_set_blob_missing, naming the source.
  • Old record lists are deleted after each accepted sync. After a sync is
    accepted, the source's older record-set and cursor copies that no saved
    position or held run names are deleted once they are older than a grace
    window. Before, only the manual okg db prune-source-blobs removed them (about
    0.5 GB a day on one large deployment). Set the window with
    source_blob_prune_grace_seconds in deployment.yaml (default 21600, that
    is six hours; 0 turns the cleanup off). The sync report shows the rows and
    bytes removed under record_set_blob_prune. A failed cleanup does not fail
    the sync; okg doctor warns source_record_set_blob_prune_failed for that
    source until a later cleanup succeeds. Do not set a very short window while
    inline or command-line runs of the same source can overlap: a sync that
    outlives the window can lose its copies.

Reading the graph: what agents get

AKMON's MCP server is how an AI agent reads a graph. The server still lists
eight tools: inspect, search, expand, filter, map, aggregate,
query and run.

Finding things and saying why not

  • Empty search and filter pages say why they are empty. Before, the reply
    said only "0 rows". Now it names the cause: a record type the graph does not
    have (with the real types listed), the condition that removed every match, a
    query word that matches nothing, or the query itself. It also lists up to
    three ids found with that part dropped, marked "loosened" and never counted. A
    check that runs out of time says so and claims nothing. Under access rules, a
    record the reader cannot see changes nothing in the reply. Pages with rows
    are unchanged and cost no extra reads.
  • A grep list row shows every matched line, up to three. Searching for exact
    text lists one row per file or record. Before, a row showed only the first
    matching line, so the line that held the answer could be hidden. Now a row
    shows all matching lines up to three and says how many more it left out. A
    reply over its size limit drops these extra lines before it drops any row. A
    typical grep page is about a third larger.
  • A search profile's excluded paths now apply to text chunks. A profile
    that excludes tests/** already left out documents under tests/, but the
    chunks of those documents (which carry no path) were still found and shown.
    A chunk is now left out when every document it belongs to is excluded, inside
    the database query, so excluded chunks cannot crowd kept ones off a page and
    counts match what is shown. Under access rules (audit or enforce mode) the
    check runs after the query, so a page can come up short; it then says it is
    cut. These searches cost somewhat more.

Refusals that show the fix

  • A refusal names the one changed call that fixes it. When a read tool
    turns down a call and one small change would make it work, the reply shows
    that corrected call, ready to send, in retry:, in error.retry_call and in
    fix_hint. About twenty refusal kinds now do this. The server picks the call
    without reading any data, offers none when the call has a second problem, and
    marks a call that is one of several possible fixes as "a guess". A shown call
    passes every check that reads no data; it is not proven to return rows.

Exact numbers and links

  • Stored sum, min and max, and one total. aggregate over stored
    records now serves sum, min and max over up to 50,000 records, and
    by=[] gives one total over everything. The reply names what each total
    covers and which values were skipped, with record ids. A misspelled record
    type is refused with the closest names instead of being called "a stored
    set". This is not served under access rules yet.
  • One strict rule for what counts as a number. Live and stored totals, live
    filters, and computed columns that read a field as a number now read text as
    a number only when it is exactly one JSON number. Values such as " 10 ",
    "+5", "010", ".5" and "1_000" are skipped and named in the reply (a
    filter names them in not_comparable). To read them anyway, use
    map(compute={"n": "number(attrs.x)"}). Numbers group by numeric value, so 1
    and 1.0 are one group.

Several reads in one call (run)

  • run counts links from an earlier step's records. A step can name the
    records an earlier step selected as the start of a link count, for example
    "how many of these commits changed each file":
    {"aggregate": {"input": {"kind": "edges"}, "where": {"edge_type": "affects", "src": "$1"}, "by": ["dst"], "metrics": ["count"]}}. Before, a join reached
    each file once, so a count after it said 1 per file, and naming the commits
    as the links' start was refused. On a 3-million-link test graph the
    most-changed-files question runs in 0.3 s.
  • run computes columns on a step's rows. A step can add computed columns,
    such as a ratio, to the rows an earlier step holds, using the same formula
    language, limits and row states as map(compute) outside run, with no
    outside call. Later steps read them as calc.<name> to filter, rank, total or
    call by. Before, this was refused ("does not take compute"). The same change
    fixes filters on an aggregate step's text group keys (for example
    site = "X"), which matched 0 rows with no note. Computed columns over
    stored graph records still wait for a later release.
  • A join step reads its start set and links once. A run step that follows
    links from an earlier step's records finishes in about a second on the test
    graph; for 91 commits it took 5.9 s and was refused for time before, and the
    whole call now takes 1.4 s, with the same records and counts.
  • run accepts the call shapes models send. It now takes a small
    max_bytes (raised to the 4096 minimum, with a note), retain on a live step
    (ignored, with a note), a kept answer (l1.…) as a step input or each
    source, input: "$n" on graph records, and constants beside each's varying
    value. The reply says what it adjusted. A kept answer's upstream calls count
    toward the run's 1,500-call limit. Of 28 recorded evaluation calls that 0.4.0
    refused, 16 now run fully.

Benchmarks and tracing

  • Each logged MCP call can carry a benchmark question and try. A test
    harness may send the question id and try number with each request (headers
    X-OKG-Question and X-OKG-Try, or okg/question and okg/try in the
    request's _meta). The server stores them on the call row (question_id,
    try_number, shown in okg.v_mcp_calls); concurrent calls keep their own
    labels. A question is one printable-ASCII token of 1 to 200 characters with no
    # or ,; a try is 1 to 999999. A badly formed label refuses the call with a
    named error. Calls logged before the upgrade keep an empty label.
  • A new experiment branch keeps the attribute values its base had. An
    experiment branch is a separate copy of the graph used to try a change.
    Before, attribute values set by observations (stored corrections layered over
    the graph) were silently dropped from the branch on its first publish, so the
    branch served the original value while the default branch served the observed
    one. The branch now serves the observed value. Observations made later on the
    branch show only on the branch. A refused branch read now names the branch.
  • **Wording the server shows an agent can be...
Read more

AKMON v0.4.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 16:13

AKMON 0.4.0 follows 0.3.0. It keeps one copy of the graph's attribute data
instead of four (one test database went from 29.6 GB to 16.3 GB). It reworks
how AI agents read a graph: exact record ids rank first, a missing name is
reported as missing, the instructions name only the records a deployment has,
groups can be ranked, and every read of an outside system can be reused,
totalled and computed over without asking it again. It keeps a
source's other records publishing when one link cannot be resolved, rebuilds a
view only when its inputs changed, and keeps the scheduled publisher from
stalling behind a long sync. Upgrading applies three database migrations, one
of which deletes the old attribute copies: back up first, and read "Upgrading
from 0.3.0". AKMON's Python package and command are named okg, so every
command below starts with okg.

Get it

  • Python package: the wheel okg-0.4.0-py3-none-any.whl is attached to
    this release. Install it with uv tool install or pip install, then
    follow the quickstart (docs/start/quickstart.md).
  • Containers and Kubernetes: the Install section at the end lists the
    runtime and PostgreSQL images and the Helm chart, each with its digest. The
    PostgreSQL image is unchanged from 0.3.0.

What's new

Storage

  • One copy of the attribute data. Before, every publish wrote the
    attribute values of each record into three stored copies besides the graph
    itself, checked them against each other, and refused some publishes until a
    repair ran. Reads now come only from the graph's versioned records plus a
    small per-version overlay, and publish no longer writes the copies. A
    migration deletes them (see Upgrading). On a 29.6 GB test copy the
    database went to 16.3 GB, and the next publish was correct.
  • Generations made by a fast-forward, a history compaction or a rollover
    keep their overlays
    and are read normally. Before, the next publish could
    lose them.

Reading the graph: what agents get

AKMON's MCP server is how an AI agent reads a graph. This release changes what
an agent finds, how much it has to read to find it, and how much it can trust
a number it is given. The server still lists eight tools: inspect, search,
expand, filter, map, aggregate, query and run.

Finding the right record

  • An exact record id ranks first. Searching for one id, such as a report
    number like ABC-12-345, lists the record that carries it first and marks it
    as an exact match. A record whose id differs only in case or separators comes
    next, marked as a variant. A record type can name the fields that hold its
    ids. Before, such a record could rank fourth.
  • A search for one code name (with an underscore, a dot or CamelCase) finds
    the record bearing that name through the attribute index instead of reading
    every text that repeats it.
  • Search says plainly when no record has a name. When an agent searches
    for one exact name or id that no record has, the reply names what was
    checked (names, ids and text, in any spelling) and lists ids with similar
    names; a lookup of a missing record id says "no record has id X". Before,
    the reply said only "rows 0 of count unknown", and agents guessed. The line
    never appears under access rules or with live data sources, nor for a
    one-word name. On graphs above about 150,000 records it needs the index that
    okg migrate builds (see Upgrading).

Reading less to get the answer

  • The instructions name only the record kinds a deployment has. doc:
    text records are mentioned only on deployments that declare a document
    source, and file: and commit: ids and source_file lookups only on
    deployments that declare a git source. Before, every deployment was told to
    use them, and the reads failed. A source module of your own that wraps
    DocCorpusSource must set mcp.doc_text_nodes: true; one that wraps a git
    source must set mcp.file_nodes: true and, for history, mcp.commit_nodes: true.
  • The dataset overview keeps the graph's four largest sources. The short
    overview an agent reads first used to hide the largest sources behind a
    "+N more sources" line once the server offered run, because run's
    example took their room. It now names the same four sources with or without run. When
    the run example does not fit beside them, the example is left out and one
    line says where the step format is described.
  • inspect on a code function points the agent at just the function's
    lines in the file's document, not the whole file.
  • Agents can read how a record changed. inspect takes two new targets:
    node_history (each version of a record in a time window) and
    subtype_diff (the records of one type that were added, removed or changed
    between two generations). Each takes one call instead of one read per
    generation and passes the same per-reader access filter as other reads.
  • Python code chunks start at their first non-blank line, so a cited
    start_line is the line the definition is on (a decorated function starts at
    its decorator). Existing graphs keep the old chunks until each file next
    changes; the first edit to a file re-embeds most of its chunks.

Exact numbers instead of arithmetic by hand

  • aggregate ranks groups. order_by on a metric (for example
    "max(attrs.size) desc") ranks every group before limit and offset cut
    the page, on stored records, on live results and inside run. Each group
    gets its rank, and the reply says whether all groups were ranked or only
    those from a partial read. Replies without order_by are unchanged.
  • Every live read returns a handle. A read of an outside system through
    filter, map, aggregate or expand now returns a handle: a short id for
    the rows the server keeps for the caller. The next call passes it as input
    and rereads those rows without asking the outside system again. An
    aggregate's groups get a handle too, so they can be filtered and sorted.
    Kept rows last 10 minutes; a session keeps up to 64 sets. A full server drops
    the busiest session's oldest set instead of refusing, and another session
    cannot read the rows. Before, rows were kept only after more than 20 outside
    calls or a cut reply, for 5 minutes, 4 per caller.
  • One handle covers a run of one-at-a-time calls. When an agent asks an
    outside system one value at a time, from the third call the reply names one
    handle for all the answers and two ready calls that total or rank them, with
    no new outside call.
  • map adds computed columns. An agent can have the server compute a
    value for each row of a live result, such as fill = used / total, then
    filter, sort or total by it. Stored (non-live) records come in a later
    release.
  • Totals over a cut-short answer say they are short. When an outside system
    sends only part of an answer, a count says it "is at least the value shown",
    and a sum, minimum or maximum says it is over the rows received only. Before,
    such a total read like a whole total.

Clear refusals

  • A failed read of an outside system says so, and "nothing matched" is an
    answer.
    When a live read fails, the reply says so on its first line and is
    marked as an error, with the remedy. Before, a failure could sit inside a
    normal-looking reply, and an agent could take it for an answer. When the
    outside system answers that nothing matched or that a name was not found,
    the reply is now zero rows with the system's own words, not an error. In a
    one-value-at-a-time call, a value with no match gives zero rows for that value
    and the values that did answer keep their rows. A real failure still fails
    the whole call.
  • A word search over whole records no longer claims "complete" for text it
    never read.
    When the default search profile lists a document's text chunks
    but not the document, search with method: "grep" and within: "record"
    read no documents and answered "0 of 0, complete", though a search by text
    unit found them. It now reads the documents those chunks belong to and says
    so. A filter that names a chunk type next to other types is refused with two
    fixes that both run; before, it returned only the other types and said
    everything was shown. Chunks nested inside chunks are not searched.
  • Kept live rows are checked on every call. A later read of kept rows
    checks the reader's permissions again, as the first read does. A map or
    filter that names a field the kept rows lack now fetches it, or is refused
    as field_not_fetched; before, it returned empty values with no error. An
    input sent as a JSON string is read like the object form; before,
    aggregate on a live type returned zero groups without an error.
  • A refused map keeps the rows it fetched. When a map that adds
    computed columns must first fetch missing fields and is then refused, the
    fetched rows are kept under a handle and the refusal names it, so the
    corrected retry asks the outside system for nothing. Before, the rows were
    thrown away and a retry repeated the same outside calls, up to 1,500. The
    handle counts toward the caller's own limit and can push out the caller's
    oldest handle, never another caller's.
  • Calling a tool the server does not offer is refused as tool_not_served,
    with the tool name and the list of tools it serves. Before, it looked like a
    crash.
  • aggregate with input set to the text null is refused as a bad
    argument that names input. Before, it failed with an internal error.

Ingest and publish

  • A link to a record that is not there yet no longer refuses the run. When
    one source points at a record another source has not written, or has since
    removed, the pointing source keeps its other records and the link waits in a
    hidden table until the record exists. okg doctor --check deferred-edges
    reports waiting links, and `okg runtime deferred-e...
Read more

AKMON v0.3.0

Choose a tag to compare

@github-actions github-actions released this 22 Oct 12:45

AKMON 0.3.0 follows 0.2.0 with fixes for the first things a new user hits
(unreadable files that were accepted without a word, PDFs that held only a file
name, a long install printout, a quickstart that built a large image), one
security fix, and the fixes merged since 0.2.0. If you run 0.2.0, see
"Upgrading from 0.2.0". AKMON's Python package and command are named okg, so
every command below starts with okg.

Get it

  • Python package: the wheel okg-0.3.0-py3-none-any.whl is attached to
    this release. Install it with uv tool install or pip install, then
    follow the quickstart (docs/start/quickstart.md).
  • Containers and Kubernetes: the Install section at the end lists the
    runtime and PostgreSQL images and the Helm chart, each with its digest. The
    chart names both images, so an install needs no image settings
    (docs/operate/deployments/kubernetes.md).

What's new

First-run fixes

  • okg deployment add-source warns when a folder holds files AKMON cannot
    read. The preview and the result name each unreadable file type with its
    count, list the formats read today, and say what to do. When none of the
    matched files can be read, it refuses instead of publishing records that hold
    only file names.
  • PDF text is searchable. A local folder source reads the text of each PDF
    page by page, so a search for a word inside a paper finds it, with the page
    number. It reads the text layer only (not scanned pages, figures or tables),
    up to 50 MiB and 5,000 pages per file. The preview warns about a PDF with no
    text, such as a scan, and about a PDF that will not open; a file over the size
    limit is recorded with no text and a warning. With the default settings,
    reading PDFs makes no model call. See "Breaking changes" for which graphs
    start reading PDFs.
  • The README is now short. It says what AKMON is (a consistent, versioned
    knowledge base of an organization's data, read by people and agents), the
    problem it solves and the pipeline in one line, then points to the
    quickstart and the documentation.
  • GitHub sources can import discussion. include_discussion=true now
    publishes issue and pull request comments; before, every import with it was
    refused. The new optional setting commit_repo names the git repository in
    the same graph and links merged pull requests to their merge commits. It
    accepts a git source that comes from a code_repos block, and refuses one
    that is switched off or missing, with the fix. Without commit_repo, merged
    pull requests import unlinked and the import step says so.
  • The quickstart installs a tagged release and starts the published database
    image instead of building one, on Docker or podman. One variable,
    AKMON_VERSION, picks the release. OKG_PG_IMAGE names the database image
    for the bundled Compose file, so two checkouts no longer retag each other's
    image.
  • The quickstart and the first-run troubleshooting page now cover the gaps a
    first-time reader hit on a cold run: a database check that could wait
    forever, the podman error about the limit on kernel keys (the fix is
    keyring = false under [containers] in containers.conf), the line
    search profile: none (unscoped) in search output, and what to do when none
    of the four supported agents is installed.
  • okg install ends with a ten-line summary: the graph's name, how many
    records and links it holds, where the database is, one search to try, and how
    to connect an agent (with the extra step for a database on another host). --verbose brings back the full report; --json has the same
    keys as before. In an activated virtual environment, hints say okg, not
    uv run okg.
  • okg agent connect --agent claude-code works on a machine where the claude
    command is not installed: it writes Claude Code's configuration and tells you
    to install Claude Code before you start a session.
  • okg jobs status, okg jobs enqueue-source, okg jobs find-source-run and
    okg chat source-retry print a one-line error that names --deployment when
    you give no deployment or database address, not a Python traceback.
  • When a source fails its health check, okg ingest and okg install now name
    the source and the connector's own reason, for example a missing credential.
    The message says whether the other sources were published without it or not
    published, and gives the command that leaves the failed source out for one
    run. That note about the other sources appears when okg ingest runs in the
    same process, not yet when the work is queued (see Known issues). A failed
    okg install names the cause instead of the tail of a log. A successful
    publish no longer warns that there was nothing to rebuild.
  • okg install --force over an existing database, when its final publish is
    refused because the database still holds the files claimed before the
    reinstall, now says so, points to okg status, and names the two commands
    to run next in order: okg catalog ownership claim, then okg run --once.
    It still exits 1 and never claims ownership itself.
  • okg chat sync no longer says that an existing chat preset cannot be
    updated. Sync does update it; each census line is now labelled as a note,
    not a write failure.

Security

  • Error and health messages, and bug reports posted to GitHub, now hide more
    kinds of secret: signed-URL values (such as ?sig=), Cookie and
    Set-Cookie header values, auth= values, Authorization credentials
    (Bearer, Basic, token, Digest and Negotiate) and token or password
    assignments. A bug report is refused before it is posted if any such value is
    left. A large slowdown when masking very long text is also fixed.
  • When a deployment's access rules are on, a live read from an outside system
    (expand with live) is now refused by name before anything leaves the
    process. See "Breaking changes".

Agents get clearer answers

  • A lookup in a live service that finds nothing says "not found", not that the
    service is down. A cut answer from a live service says where it stopped and
    how to ask for more, never "all shown".
  • A search over file names says that file contents need a different search.
  • Searching for the exact name of a record lists that record first, marked
    match_kind: exact_name. This covers the name of a class, function or
    method, and the name of any other record, such as the release
    CMSSW_14_0_0. Before, look-alike records could fill the top places, or
    only imports and references came back and the definition was missing.
    Imports and references that repeat a symbol's name follow its definition. Up
    to 50 same-name symbols are listed and counted exactly; more is a lower bound
    that says why. Under enforced access rules the count is never called exact.
    On a graph of 635,000 records the extra read added at most 28 ms to a
    search. Only a name that looks like code takes this route (see Known
    issues).
  • When an agent calls inspect with kind and node_id at the top level
    instead of inside target, the refusal now shows the corrected call, ready
    to send, with other arguments such as select and max_bytes kept. The call
    is still refused, not served.
  • A reply that goes on in another call prints a short 40-character handle for
    the next page. A refused handle names the call that restarts the read. A
    handle lives in the server process that issued it: it ends at a restart,
    after 2 hours, or when the server drops it to stay inside its limits. With a
    shared signing key set in the environment variable
    OKG_MCP_CONTINUATION_SIGNING_KEY, the reply prints the full token, which any
    server process can open.
  • okg inspect texts no longer prints a stray code fence in the middle of a
    word when a page holds two pieces of one passage.

Access rules

  • With per-user access rules on, keyword search applies the reader's rules
    before its read limits, so records a reader may not see cannot fill them. It
    also remembers each reader's access decisions instead of checking them again
    on every search; the cost measured at about 3 ms per search. Servers where
    each request names its reader, grep limits, relevance scores, search by
    meaning and records with no read class are not covered yet (see Known issues).
  • Under access rules, keyword search for one kind of record, such as documents,
    keeps using the text index instead of a slower route the database wrongly
    picked. On one test graph the database time fell from 183-221 ms to 85-104 ms
    with identical results. Four other reads still use the plain filter.

Reading data that lives in another system

  • Data held in an outside system can now be read like ordinary graph
    records, without storing it. A deployment declares a live type once: its id
    prefix, its fields, the outside calls that fill them, and its links. Agents
    then use the usual filter, map, aggregate, inspect and expand
    tools on it. The server plans the outside calls, sends the conditions the
    outside system can apply to it, fetches only the fields the call needs, and
    applies every condition itself before answering.
  • A read that cannot work is refused before any outside call: a misspelled
    field, metric, grouping or link, a missing argument, an unsafe value, a
    plan over 1,500 calls, or a pinned graph version on a live type. Text from
    the outside system is marked as untrusted before an agent sees it.
  • Each live answer starts with one line naming the system, the read time, how
    many records were read and listed, the calls made, and the conditions the
    outside system applied. A read that made more than 20 calls, or whose
    answer was cut, is kept in the server for 300 seconds, so the next call
    reads it without calling the outside system again.
  • A new tool, run, chains reads in one call: live reads, value lists,
    differences and overlaps between result sets, and totals over the rows it
    holds, with o...
Read more

AKMON v0.2.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 10:21

The first public release of AKMON. It builds a searchable knowledge graph
from your repositories, documents and services, and serves it to coding
agents (Claude Code, Claude Desktop, Cursor and Codex) over MCP, the protocol
those agents use to call tools. AKMON's Python package and command are named
okg, so every command below starts with okg.

Get it

  • Python package: the wheel okg-0.2.0-py3-none-any.whl is attached to
    this release. Install it with uv tool install or pip install, then
    follow the quickstart (docs/start/quickstart.md).
  • Containers and Kubernetes: the Install section at the end lists the
    runtime and PostgreSQL images and the Helm chart, each with its digest. The
    chart names both images, so an install needs no image settings
    (docs/operate/deployments/kubernetes.md).

What's new

Agents get better answers

  • Search finds exact names, ticket keys and file names, and lists a file
    first when you name it. Duplicate records no longer crowd out results.
  • Search results show a heading and the matching passage, not the whole
    record, and a record already shown in the session is referred to instead
    of repeated. Searches return 8 results by default.
  • Search and record replies say on their first line how large they are and
    which setting makes them smaller. A reply that would be too large comes back shortened, with the
    call that reads the rest, instead of being refused.
  • When a call is nearly right, AKMON answers it and says what it changed;
    otherwise it says what to change. A mistyped record id gets the nearest
    real one. Results say what they left out and where to find it.
  • A read never says it is complete when it left rows out, and a condition
    with a value of the wrong type is refused instead of answered with an
    empty page.

More ways to read the graph

  • Every read tool takes the same conditions, including dates, text patterns
    and an accent- and case-blind substring test, and the same field names.
  • Read up to 50 records in one call, read chosen lines of a text, and count
    records per day, week, month or year, per list element or per link.
  • expand returns the records a record links to, each with its type and
    title; * follows any link.
  • Exact text search can list every matching record with an exact total and
    gives the line number of each match.
  • The read-only SQL tool query describes the graph it serves and pages a
    large reply. It is offered when per-user access rules are off.
  • Search by meaning (optional). With vector sets configured under
    embeddings in deployment.yaml, search can rank records by meaning as
    well as by words (docs/operate/configure.md, "Embeddings").

Reading from a shell

  • The okg command line has one command per read tool: okg search,
    inspect, expand, filter, aggregate, map and query. Each prints
    the reply the MCP server would send, or JSON with --json, and exits 0
    when served, 2 when refused and 1 on any other failure.
  • The commands take short shell forms (--where status=Open, a bare record
    id) and print rows for a pipe (--jsonl, --ids, --all); ids can be
    piped from one command into the next.
  • okg <command> --help shows each tool's own description with a worked
    example, and okg guide prints the instructions an agent gets when it
    connects.

Larger answers and live services

  • An agent can page through, filter and count one large answer from a live
    service (a database or API the graph connects to) without running out of
    context.
  • An agent can keep a search result and come back to it later in the same
    session.
  • When an agent connects, a short summary tells it what the graph holds.
  • Live services that answer with a single object return it as one row.

Many agents, many users

  • One server handles several agents at once without queuing them.
  • With per-user access rules on, each reader sees only what they may read:
    search ranks only readable text, and a record a reader may not see reads
    as not found.

Setting up

  • okg install sets up a graph in one command; okg agent connect
    connects every agent it finds. install.sh installs from a clone.
  • The bundled database's port, container name and password are settings,
    and it runs under its own Compose project, okg-pg.
  • okg doctor passes on a fresh install without false alarms, checks that
    each agent's saved command still exists, and fails when the newest publish
    was refused.
  • An unreachable database is reported in one line that names the host and
    the setting to fix.
  • When an agent can't find the database address, okg agent connect says
    where it must go and prints the command that puts it there.
  • The "Run it as a service" docs lead from a working graph to a shared
    service in one path.

Keeping a graph current and safe

  • okg up upgrades an installed graph: it checks compatibility, backs up the
    database and checks the backup, migrates, and writes a record of what it
    did. okg deployment backup restore puts the backup back.
  • A publish blocked by the catalog's rules names the missing rule and the
    module that provides it. A publish check can name the sources it depends
    on; while one of them is off, the check is skipped and reported instead of
    blocking the publish.
  • okg doctor warns when a source loses most or all of its records, and
    reports a publish schedule you paused on purpose as paused, not stale.
  • Credentials that appear in stored Git diffs are hidden before they reach
    the graph, and records of deleted files say when and by which commit they
    were deleted.
  • Git fixes: each commit carries its author as plain fields; turning rename
    detection on or off applies to history already read; a file that changes
    type is one change; a branch switch with no file changes moves the files to
    the new branch; an unchanged run no longer removes file records.
  • Undoing a source run no longer deletes a later rerun's rows.
  • The outbound-fetch allow list accepts IPv6 addresses.

Breaking changes

These change how an existing installation behaves. A new install is not
affected.

  • Migrate each database once. After installing 0.2.0, run
    okg up <deployment> (or okg migrate --deployment <deployment> --apply)
    for every existing graph; the steps are below. A graph pinned with
    release.channel: prod also needs its release.pin in deployment.yaml
    updated to the 0.2.0 values; check it with
    okg deployment release <deployment> --json.
  • Move keyword search to pg_textsearch 1.5.1. Existing databases keep
    version 1.3.0, which can repeat or drop keyword-search results, until you
    move them by hand. The move is one-way, so back up first; see below.
  • Unknown source settings are refused. A source registry file
    (source_registry.yaml) now refuses settings AKMON does not recognise; the
    error names the setting and the closest valid one. Rename or remove it
    before upgrading.
  • A sync that suddenly returns nothing now fails. When the background
    worker reads a whole source and gets no records while the graph still holds
    records from it, the sync fails on every check instead of quietly keeping
    the old records. If the source really is empty now, run
    okg ingest --inline --allow-full-retraction <source> once.
  • A failed source no longer blocks publishing. A published version can now
    hold fresh data from the sources that succeeded next to older data from one
    that failed, and its ingest_completeness marker says which. okg migrate
    lifts the repair requirement the earlier behaviour left on such versions.
  • okg catalog load --apply waits for, and blocks, other writes. It now
    holds the graph's write lock while it runs, so other writes to the graph
    wait until it finishes.
  • Read replies changed shape. Scripts that parse replies should check
    three changes: a one-hop expand returns one row per linked record (select
    only link fields for the old one row per link); searches return 8 shorter
    results by default (set limit and max_chars for more); and under
    per-user access rules the SQL tool query is no longer offered.

Upgrading from an earlier build

Only if you ran AKMON from source before this release:

  1. Install the 0.2.0 wheel, then run okg up <deployment> --plan and
    okg up <deployment>. okg up needs pg_dump and pg_restore from
    PostgreSQL 17 on your PATH; --plan checks for them.
  2. Move keyword search to pg_textsearch 1.5.1 (required). Back up,
    restart the database on the 0.2.0 PostgreSQL image (the published
    akmon-postgres:v0.2.0 image in the Install section, or a rebuild of
    ops/pg), then run
    ALTER EXTENSION pg_textsearch UPDATE; in each AKMON database. Version
    1.3.0 can repeat or drop search results. okg doctor warns until this is
    done. The move is one-way; going back takes the backup.
    See docs/operate/lifecycle.md, "Upgrade to 0.2.0".
  3. Expect one long Git sync. The first sync after the upgrade reads
    every Git commit again once, to store each commit's author. On a graph
    with a long Git history this took 6 to 7 minutes, then a publish of 8 to
    10 minutes, and the database grew by about 3 GB.

New installs on the 0.2.0 image need none of these steps.

Known issues

  • Code parsing on a slow network. On first use, code parsing downloads
    about 500 MB of language grammars, and the download stops after about a
    minute. On a slow connection, download them in advance; see
    docs/runbooks/troubleshooting/first-run.md.
  • GitHub comments. The github source's include_discussion=true
    setting fails the import (producer_node_subtype_not_allowed); leave it
    off. Issues and pull requests themselves import normally.
  • Upgrade, restore and copy need PostgreSQL client tools. okg up,
    okg deployment backup restore and okg db clone need pg_dump and
    pg_restore at the database ...
Read more