Provenance, usage telemetry, recoverable archive, and rollback for agent-created skills
Agent-created skills carry provenance metadata and usage telemetry that lets you track, archive, restore, and roll back skills across their entire lifetime.
from praisonaiagents import Agentagent = Agent( name="Self-Improving Agent", instructions="Create skills when you learn something new; archive stale ones.", tools=["skill_manage", "skills_list"],)agent.start("Learn how to summarise weekly reports, then save that as a skill.")
The user teaches a workflow; lifecycle tools track creation, usage, archive, and rollback.
# Each invoke() or get_instructions() call increments use-count and sets last-usedbody = mgr.invoke("csv-analysis", raw_args="sales.csv")# SKILL.md frontmatter is updated in place:# use-count: 1# last-used: "2024-06-24T10:05:00Z"
3
Recoverable Delete and Restore
# delete_skill archives by default — the skill is NOT gonemgr.delete_skill("csv-analysis")print(mgr.list_archived_skills()) # ["csv-analysis"]# Bring it backmgr.restore_skill("csv-analysis")
4
Rollback a Broken Edit
# edit_skill and patch_skill save a snapshot before mutatingmgr.edit_skill("csv-analysis", "# CSV Analysis v2\n... broken content ...")# Undo the last changeresult = mgr.rollback_skill("csv-analysis")print(result)# {"success": True, "skill": "csv-analysis"}
Five fields are added to SKILL.md frontmatter when agent_created=True is passed to create_skill, or set manually. The parser accepts both hyphen (agent-created) and underscore (agent_created) forms.
Field
Frontmatter Key
Type
Default
Written When
agent_created
agent-created
bool
False
create_skill(agent_created=True)
created_at
created-at
str (ISO 8601)
None
create_skill() call timestamp
use_count
use-count
int
0
Incremented on each invoke() / get_instructions()
last_used
last-used
str (ISO 8601)
None
Set on each invoke() / get_instructions()
patch_count
patch-count
int
0
Incremented on each edit_skill() / patch_skill()
Backward-compatible: Skills without these fields parse fine — all fields default to their zero/None values.Telemetry only counts successful activations. If get_instructions() returns None because the skill failed to activate, use-count and last-used are not updated. A broken skill stays at use-count: 0, keeping telemetry trustworthy.
SkillProperties.idle_days returns the number of days since the skill was last meaningfully active.
from praisonaiagents import SkillManagermgr = SkillManager()mgr.discover()skill = mgr.get_skill("csv-analysis")days = skill.properties.idle_days# Returns days since last_used, or since created_at if the skill has never been used.# Returns None when neither last_used nor created_at is set.if days is not None and days > 30: print(f"csv-analysis has been idle for {days:.1f} days — consider archiving")
idle_days exposes the data for lifecycle decisions. The policy (what “stale” threshold to apply, when to auto-archive) lives in plugin code, not in core.
If a skill with the same name already exists in skills_archive/ when archiving, a UTC timestamp suffix is appended: <name>.<YYYYMMDDHHMMSS>. This prevents overwriting a previously archived version.
archive_skill() removes the skill from the in-memory index immediately. After restore_skill(), call discover() or use the returned path to add it back manually if needed in the same session.
from praisonaiagents import SkillManagermgr = SkillManager()mgr.discover()# Archivemgr.archive_skill("csv-analysis")print("csv-analysis" in mgr.skill_names) # False — removed from index# Restoremgr.restore_skill("csv-analysis")mgr.discover() # re-index after restoreprint("csv-analysis" in mgr.skill_names) # True
patch_skill(name, old, new) when file_path is None or "SKILL.md" — saves prior SKILL.md
What does NOT save a snapshot:
patch_skill(name, old, new, file_path="scripts/helper.py") — modifying a non-SKILL.md file does not create a .skill.bak
Error when no snapshot exists:
result = mgr.rollback_skill("csv-analysis")# {"success": False, "error": "No rollback snapshot for skill 'csv-analysis'"}
Each new edit_skill or patch_skilloverwrites the previous .skill.bak. Rollback is single-step only — you cannot roll back through multiple prior versions.
from praisonaiagents import Agent, SkillManager# Agent creates and improves skills over timeagent = Agent( name="Self-Improving Agent", instructions=""" When you learn something new, create a skill for it. When a skill becomes outdated, archive it. """, tools=["skill_manage", "skills_list"])# The agent's skill_manage calls automatically:# 1. stamp created-at, use-count, patch-count on creation# 2. increment use-count on each use# 3. archive (not delete) when retiring a skillagent.start("Learn how to summarise weekly reports, then use that knowledge.")
delete_skill() archives by default. Only pass hard=True when you mean it. The archive store lets you recover skills that were accidentally retired.
Don't Chain Edits Expecting Multi-Step Undo
Each edit_skill or patch_skill overwrites .skill.bak. If you need to preserve multiple prior versions, copy SKILL.md externally before each edit, or use a version control system for the skills directory.
Telemetry Counts Successful Activations Only
get_instructions() returns None when a skill fails to activate, and in that case use-count and last-used are not updated. A skill that is broken will not appear to be active in your telemetry — safe to archive without inflating its apparent usage.
Curator Policy Belongs in Plugins
idle_days and the provenance fields expose the data needed to make lifecycle decisions. The policy — what idle threshold triggers archiving, whether agent-created skills get different treatment — belongs in plugin code or your own scripts, not in application code. This keeps core behaviour predictable.
Verify After Restore
After restore_skill(), call mgr.discover() (or mgr.add_skill(path)) to re-index the skill in the current session. restore_skill() moves the directory but does not automatically re-add it to the in-memory index.