Skip to content

Manage Extensions

Extensions are installable modules — protocol decoders, hardware drivers, analyzers — that the agent loads on demand. The SDK lets you discover what's installed, inspect each one, and start, stop, or restart them.

Building your own extension?

This page covers managing installed extensions. To develop a new one, see How to Develop Extensions.

The snippets below assume an agent connected via connect():

from zelos_sdk import connect

with connect() as agent:
    ...

Examples use <ext-id> as a placeholder — substitute the publisher-qualified ID of an extension you have installed (agent.extensions.list() will show what's there).

List installed extensions

for ext in agent.extensions.list():
    print(f"{ext.id:45} {ext.version:8} {ext.state}")

Each entry carries enough metadata to render a marketplace-style list:

Field Description
id Publisher-qualified ID like "publisher.extension-name".
version SemVer of the installed copy.
state ExtensionState — "installed" or "running".
pid OS process ID when running, otherwise None.
last_exit ExitInfo (.code / .signal) when the last run ended on its own, without a stop() call. None after stop(), after a new start, or when it never ran.
name, description Display strings.
author, repository, homepage Marketplace links (any may be None).
keywords, categories For grouping and filtering.
host_type Where it runs — typically "agent".
dev_mode True for locally-installed development builds.
zelos_version Compatibility range it was built for.
app_contribution_kind, entry, icon_path Display and launch metadata.

Filter by state for display:

running = [e for e in agent.extensions.list() if e.state.value == "running"]
print(f"{len(running)} extension(s) currently running")

If last_exit is set on an extension that isn't running, the previous run ended without a stop() call:

for ext in agent.extensions.list():
    if ext.state.value != "running" and ext.last_exit is not None:
        print(f"{ext.id} exited: code={ext.last_exit.code} signal={ext.last_exit.signal}")

Inspect an extension

agent.extensions.info(id) returns an ExtensionInfo for one extension. It carries the same manifest and state fields as an ExtensionEntry, plus install_path (the directory the extension is installed in) and readme_path. It does not carry author or last_exit; read those from list().

info = agent.extensions.info("<ext-id>")

print(info.name, info.version)
print("installed at:", info.install_path)
print("state:", info.state)
print("categories:", info.categories)

info(id) resolves to the currently-installed version automatically. Pass version="1.2.3" to pin a specific version. The same auto-resolution applies to readme(), config_schema(), and last_config().

agent.extensions.readme(id) returns the extension's README markdown — or "" if it doesn't ship one:

readme = agent.extensions.readme("<ext-id>")
print(readme or "_No README provided._")

Inspect config

Two helpers expose configuration state:

  • config_schema(id) — JSON Schema dict describing config inputs, or None if none declared.
  • last_config(id) — last config saved, or None if never configured. An empty dict means "configured with no fields".
schema = agent.extensions.config_schema("<ext-id>")
last = agent.extensions.last_config("<ext-id>")

if schema is None:
    print("Extension has no config schema.")
else:
    print("required fields:", schema.get("required", []))
    print("last saved config:", last)

To render a config form, walk schema["properties"] for the field list and pre-fill from last_config (or each property's default).

Start, stop, restart

ext_id = "<ext-id>"

# Start with a config (saved as the new last_config)
result = agent.extensions.start(ext_id, {"setting": "value"})
print(f"started {result.id}@{result.version} (pid={result.pid})")

# Stop (idempotent)
agent.extensions.stop(ext_id)

# Restart with the saved config
agent.extensions.restart(ext_id)

# Restart with a different config
agent.extensions.restart(ext_id, {"setting": "another"})

start() and restart() return an ExtensionStart with .id, .version, and .pid.

config takes three shapes:

  • config=None (default) — keep the saved config. The extension starts with its saved config.json, unchanged and not validated again.
  • config={...} — validate against the schema, then save it as the new last_config. An invalid config raises ValueError, and restart() leaves the running extension running.
  • config={} — validate an empty config, then save it. This fails if the schema declares required fields.

After a lifecycle call, agent.signals() updates automatically to reflect the change.

Signals may lag the start call

start() returns when the agent accepts the request — not when signals are flowing. Poll agent.signals() or check agent.extensions.info(id).state for "running" if you need to confirm.

Worked example

A typical loop — start an extension, watch its signals come online, then stop it cleanly.

import time

ext_id = "<ext-id>"

# Start with the saved config
agent.extensions.start(ext_id)

# Give the extension a moment to publish its signals
time.sleep(1.0)
print(f"{len(agent.signals())} signal(s) live")

# When you're done
agent.extensions.stop(ext_id)

Once running, agent.actions.list() will include any actions the extension publishes — call them via agent.actions.execute(...) (see Run Actions).

Errors

from zelos_sdk import AgentError

try:
    agent.extensions.start("<ext-id>", {"setting": "value"})
except ValueError as exc:
    print("config rejected:", exc)
except AgentError as exc:
    print("could not start:", exc, "cause:", exc.cause)
Exception When
ValueError start() or restart() got a config that fails the extension's schema. A config that cannot be encoded as JSON raises AgentError.
ExtensionError info(), readme(), config_schema() or last_config() named an extension that is not installed; the agent reported that the extension failed to start; the saved schema or config is not a JSON object.
AgentError start(), restart() or stop() named an extension that is not installed, or the call failed on the agent. .cause carries the agent's error code and message. ExtensionError is a subclass, so except AgentError catches both.
TypeError config is not a dict.

Tips

  • To restart with the saved settings, call restart(id) with no config.
  • After start(), check info(id).state.value == "running" (or look for new entries in agent.signals()) before relying on the extension's signals or actions.
  • dev_mode=True flags locally-installed development builds — useful for tooling that should treat them differently from marketplace installs.

What's next

  • Run Actions

    Call typed actions exposed by the extensions you've started.

  • Query Live Data

    Time-series queries, latest values, and replay windows.

  • Develop Extensions

    Write your own extension, package it, and publish it to the marketplace.