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():
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¶
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:
Inspect config¶
Two helpers expose configuration state:
config_schema(id)— JSON Schema dict describing config inputs, orNoneif none declared.last_config(id)— last config saved, orNoneif 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 savedconfig.json, unchanged and not validated again.config={...}— validate against the schema, then save it as the newlast_config. An invalid config raisesValueError, andrestart()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(), checkinfo(id).state.value == "running"(or look for new entries inagent.signals()) before relying on the extension's signals or actions. dev_mode=Trueflags locally-installed development builds — useful for tooling that should treat them differently from marketplace installs.
What's next¶
-
Call typed actions exposed by the extensions you've started.
-
Time-series queries, latest values, and replay windows.
-
Write your own extension, package it, and publish it to the marketplace.