Modular prompt & skill contribution¶
CONCEPT:AU-OS.deployment.agent-factory-autoload (entry-point discovery) · CONCEPT:AU-ORCH.routing.resolve-body-single-canonical (canonical prompt schema) · CONCEPT:AU-KG.compute.user-override-prompt-library (KG prompt-library ingestion + XDG overlay)
Why¶
agent-utilities and universal-skills used to be the only homes for system
prompts (~90 JSON blueprints in agent_utilities/prompts/) and skills (~330 in
universal-skills). The ~63 agent-packages under agent-packages/agents/*
carried almost nothing of their own. That made the hub heavy and coupled every
agent's prompt/skills to a central repo.
This subsystem inverts the topology: each agent-package ships its own system prompt(s) and skills inside its own wheel, and the hub discovers them. The hub stays lean (it gains discovery code, not assets); a package is modular and self-contained.
How discovery works¶
Any package opts in by declaring two setuptools entry-points pointing at data-only subpackages:
[project.entry-points."agent_utilities.skill_providers"]
servicenow-api = "servicenow_api.skills"
[project.entry-points."agent_utilities.prompt_providers"]
servicenow-api = "servicenow_api.prompts"
The hub resolves each entry-point from its owning distribution's file manifest and
RECORD; provider modules are never imported. The target must be a data-package
directory owned by that same distribution. Duplicate names, including portable
case-fold collisions, fail closed before mutation. A provider-local missing or invalid
source is isolated and reported unhealthy without making stale XDG content current.
flowchart TD
subgraph pkg["agent-package wheel (servicenow-api)"]
SK["servicenow_api/skills/**/SKILL.md"]
PR["servicenow_api/prompts/*.json"]
EP["pyproject entry-points:\nskill_providers / prompt_providers"]
end
EP -. importlib.metadata .-> RES["core.providers\nvalidated current resolvers"]
subgraph hub["agent-utilities (lean hub)"]
RES --> MAT["core.unified_install"]
RES --> INST["toolkit installer"]
RES --> ING["registry_builder.ingest_prompts_to_graph()"]
end
SK --> MAT
SK --> INST
PR --> ING
MAT --> XDG["XDG data: skills/provider/.generations/digest/skill\n(v2 activation marker)"]
INST --> TOOLS["detected agent tools"]
ING --> KG[("KG prompt library\nPromptNode prompt:<pkg>/<name>")]
OVL["~/.config/agent-utilities/prompts/\n(operator XDG overlay)"] --> ING
BASE["base prompts\nexact XDG generation or current source"] --> ING
Skills → XDG skills library¶
agent-utilities install materializes every current provider into the XDG data tree
and then installs the selected toolkit into detected external agent tools. Each
skills/<provider>/ root holds immutable content-addressed generations. A closed,
bounded, path-free schema-v2 marker atomically selects one generation and records the
registration digest, content digest, file count, and byte count. Writers are
serialized and build a private, fsynced stage before activation, so readers observe a
complete old or complete new generation.
An unmarked destination is operator-owned even when its name collides with a provider;
installation fails without replacing it. Flat skills/<skill>/SKILL.md directories
remain operator-owned and are never pruned. A root containing any provider marker
entry—current, inactive, corrupt, or retired—is never reinterpreted as a flat skill.
Duplicate declared skill identities fail closed. Sources containing links, junctions,
special files, reserved markers, escaping paths, or assets beyond the bounded manifest
limits are rejected. A zero-asset registration is atomically deactivated instead of
retaining its previous content.
At runtime the installed distribution registration and live source manifest are revalidated against the marker and generation. Package upgrades, tampering, empty generations, and unresolved sources therefore cannot make old XDG content current; validated live source is used when available, otherwise that provider is unavailable.
Prompts → KG prompt library¶
ingest_prompts_to_graph() ingests prompts in precedence order (later overrides
earlier on the namespaced id):
- validated base — exact current XDG generation or packaged source →
prompt:<name> - fleet-contributed — each
prompt_providersdir →prompt:<provider>/<name> - operator overlay —
prompts_dir()(~/.config/agent-utilities/prompts/)
The canonical prompt schema¶
The single source of truth is the Pydantic model
agent_utilities.prompting.structured.StructuredPrompt. The body lives in
instructions.core_directive. Alternate flat body keys are rejected. The
canonical fields are schema_version, prompt_version, source,
skills, extends (+ compose). One resolver resolve_body() and one
validator validate_canonical() back every consumer:
- the three readers in
prompting/builder.py+agent/registry_builder.py(this fixed a real bug where decomposed prompts extracted an empty body); - the
prompt-builderskill (build_prompt.py/validate_prompt.py); - the CI gate
scripts/check_prompt_schema.py+ generatedprompting/prompt.schema.json(scripts/gen_prompt_schema.py); - per-package/scaffold parity tests.
A package prompt sets extends: "agent-utilities:base" + compose: append to
inherit the base prompt at render time (build_system_prompt_from_workspace).
Authoring / scaffolding¶
agent-package-builder now scaffolds the whole contribution (canonical
prompts/main_agent.json, a starter skill, entry-points, package-data,
MANIFEST). prompt-builder authors/validates individual prompts. Existing
packages are brought up to standard idempotently by
scripts/retrofit_fleet_contribution.py.
Keep-lean guarantee¶
Assets live in each contributor's wheel; the hub only resolves + indexes them.
Adding the Nth provider adds zero bytes to agent-utilities/universal-skills,
no new hub dependencies (stdlib importlib.metadata), and the heavy ML deps of
any agent never reach the hub serving path. Resolution is non-importing: the hub
accepts only selected files listed by the entry point's owning distribution
(RECORD/distribution file manifest) and then applies the bounded regular-file
manifest contract. An injected but unrecorded file fails closed.