Perplexity's Agent Skills Need an Undo Path Before They Need More Skills

Perplexity's Agent Skills Need an Undo Path Before They Need More Skills

Perplexity added "Skills" to its Agent API, letting developers compose built-in and custom skills for more complex agent outputs. More skills mean more actions, and more actions mean more ways to produce an irreversible change. Before you add a fifth skill to your agent, make sure the first four have an undo path. The undo problem An agent skill that writes, sends, publishes, or deploys is an irreversible action if there is no rollback. The more skills an agent has, the more irreversible actions it can take in a single session. A chain of skills (research → draft → format → publish) can complete before a human notices the first step was wrong. The undo contract Every agent skill should declare: { "skill_name": "publish_to_blog", "action_type": "write", "reversible": true, "undo_method": "set_published_false", "undo_timeout_seconds": 3600, "undo_side_effects": ["SEO index will retain the URL for up to 24h"] } Enter fullscreen mode Exit fullscreen mode If reversible is false, the skill should require explicit human approval before execution. A skill registry with undo support # skill_registry.py """ Registers agent skills with undo metadata. Blocks irreversible skills from running without explicit approval. """ from dataclasses import dataclass from typing import Optional, Callable import json @dataclass class Skill: name: str action_type: str # "read", "write", "send", "deploy" reversible: bool undo_fn: Optional[Callable] = None undo_timeout_seconds: int = 3600 side_effects: str = "" requires_approval: bool = False class SkillRegistry: def __init__(self): self.skills = {} self.execution_log = [] def register(self, skill: Skill): # Irreversible write/send/deploy skills require approval if not skill.reversible and skill.action_type in ("write", "send", "deploy"): skill.requires_approval = True self.skills[skill.name] = skill def execute(self, skill_name: str, inputs: dict, approved: bool = False): skill = self.skills.get(skill_name) if not skill: raise ValueError(f"Unknown skill: {skill_name}") if skill.requires_approval and not approved: return { "status": "blocked", "reason": "Skill is irreversible and requires explicit approval", "skill": skill_name, "action_type": skill.action_type } # Execute the skill (simulated) result = {"status": "executed", "skill": skill_name, "inputs": inputs} self.execution_log.append({ "skill": skill_name, "inputs": inputs, "result": result, "undo_available": skill.reversible, "executed_at": "2026-07-20T15:00:00Z" }) return result def undo(self, execution_index: int): entry = self.execution_log[execution_index] skill = self.skills[entry["skill"]] if not skill.undo_fn: return { "status": "failed", "reason": "No undo function registered", "skill": entry["skill"] } undo_result = skill.undo_fn(entry["inputs"]) entry["undone"] = True entry["undo_result"] = undo_result return {"status": "undone", "skill": entry["skill"], "result": undo_result} # Example usage if __name__ == "__main__": registry = SkillRegistry() # Reversible skill def undo_publish(inputs): return f"Set published=false for {inputs.get('post_id')}" registry.register(Skill( name="publish_post", action_type="write", reversible=True, undo_fn=undo_publish, side_effects="SEO index retains URL for 24h" )) # Irreversible skill registry.register(Skill( name="send_email", action_type="send", reversible=False, undo_fn=None, side_effects="Email cannot be un-sent" )) # Test reversible skill result1 = registry.execute("publish_post", {"post_id": "123", "title": "Test"}) print(f"Publish: {result1}") # Undo it undo1 = registry.undo(0) print(f"Undo publish: {undo1}") # Test irreversible skill without approval result2 = registry.execute("send_email", {"to": "user@example.com"}) print(f"Send without approval: {result2}") assert result2["status"] == "blocked" # Test with approval result3 = registry.execute("send_email", {"to": "user@example.com"}, approved=True) print(f"Send with approval: {result3}") assert result3["status"] == "executed" # Try to undo (should fail) undo2 = registry.undo(1) print(f"Undo send: {undo2}") assert undo2["status"] == "failed" print("\nPASS: undo contract enforced") Enter fullscreen mode Exit fullscreen mode What the registry enforces Skill type Execution Undo Read-only Always allowed Not needed Reversible write Always allowed Available within timeout Irreversible write Blocked without approval Not available What to check List every skill your agent currently has. For each one, fill in the undo contract above. If you have a skill where reversible is false and requires_approval is not enforced, you have an irreversible action running without a safety gate.

Original Source

Read the full article at Dev →

KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.