Self modification
This is the logical extension of "whatever the developer can do, the agent can do."
Why Self-Modification?¶
Traditional software is static—it does what you wrote, nothing more. Self-modifying agents can:
- Fix their own bugs - See an error, patch the code, restart
- Add new capabilities - User asks for something new, agent implements it
- Evolve behavior - Learn from feedback and adjust prompts
- Deploy themselves - Push code, trigger builds, restart
The agent becomes a living system that improves over time, not frozen code.
What Self-Modification Enables¶
Code modification: - Read and understand source files - Write fixes and new features - Commit and push to version control - Trigger builds and verify they pass
Prompt evolution: - Edit the system prompt based on feedback - Add new features as prompt sections - Refine judgment criteria that aren't working
Infrastructure control: - Pull latest code from upstream - Merge from other branches/instances - Restart after changes - Roll back if something breaks
Site/output generation: - Generate and maintain websites - Create documentation - Build dashboards from data
Required Guardrails¶
Self-modification is powerful. It needs safety mechanisms.
Approval gates for code changes:
tool("write_file", async ({ path, content }) => {
if (isCodeFile(path)) {
// Store for approval, don't apply immediately
pendingChanges.set(path, content);
const diff = generateDiff(path, content);
return { text: `Requires approval:\n\n${diff}\n\nReply "yes" to apply.` };
}
// Non-code files apply immediately
writeFileSync(path, content);
return { text: `Wrote ${path}` };
});
Auto-commit before changes:
tool("self_deploy", async () => {
// Save current state first
runGit("stash"); // or commit uncommitted changes
// Then pull/merge
runGit("fetch origin");
runGit("merge origin/main --no-edit");
// Build and verify
runCommand("npm run build");
// Only then restart
scheduleRestart();
});
Build verification:
// Don't restart unless build passes
try {
runCommand("npm run build", { timeout: 120000 });
} catch (error) {
// Rollback the merge
runGit("merge --abort");
return { text: "Build failed, aborting deploy", isError: true };
}
Health checks after restart:
tool("health_check", async () => {
const uptime = process.uptime();
const buildValid = existsSync("dist/index.js");
const gitClean = !runGit("status --porcelain");
return {
text: JSON.stringify({
status: "healthy",
uptime: `${Math.floor(uptime / 60)}m`,
build: buildValid ? "valid" : "missing",
git: gitClean ? "clean" : "uncommitted changes",
}, null, 2),
};
});
Git-Based Self-Modification¶
Use git as the foundation for self-modification. It provides: - Version history (rollback capability) - Branching (experiment safely) - Merge (sync with other instances) - Push/pull (deploy and collaborate)
Essential git tools:
tool("status", "Show git status", {}, ...);
tool("diff", "Show file changes", { path: z.string().optional() }, ...);
tool("log", "Show commit history", { count: z.number() }, ...);
tool("commit_code", "Commit code changes", { message: z.string() }, ...);
tool("git_push", "Push to GitHub", { branch: z.string().optional() }, ...);
tool("pull", "Pull from GitHub", { source: z.enum(["main", "instance"]) }, ...);
tool("rollback", "Revert recent commits", { commits: z.number() }, ...);
Multi-instance architecture:
main # Shared code
├── instance/bot-a # Instance A's branch
├── instance/bot-b # Instance B's branch
└── instance/bot-c # Instance C's branch
Each instance can: - Pull updates from main - Push improvements back to main (via PR) - Sync features from other instances - Maintain instance-specific config
Self-Modifying Prompts¶
The system prompt is a file the agent can read and write.
// Agent can read its own prompt
tool("read_file", ...); // Can read src/prompts/system.md
// Agent can propose changes
tool("write_file", ...); // Can write to src/prompts/system.md (with approval)
System prompt as living document:
## Feedback Processing
When someone shares feedback:
1. Acknowledge warmly
2. Rate importance 1-5
3. Store using feedback tools
<!-- Note to self: Video walkthroughs should always be 4-5,
learned this from Dan's feedback on 2024-12-07 -->
The agent can: - Add notes to itself - Refine judgment criteria - Add new feature sections - Document edge cases it learned
When to Implement Self-Modification¶
Good candidates: - Long-running autonomous agents - Agents that need to adapt to feedback - Systems where behavior evolution is valuable - Internal tools where rapid iteration matters
Not necessary for: - Simple single-task agents - Highly regulated environments - Systems where behavior must be auditable - One-off or short-lived agents
Start with a non-self-modifying prompt-native agent. Add self-modification when you need it.
Complete Self-Modification Toolset¶
const selfMcpServer = createSdkMcpServer({
name: "self",
version: "1.0.0",
tools: [
// FILE OPERATIONS
tool("read_file", "Read any project file", { path: z.string() }, ...),
tool("write_file", "Write a file (code requires approval)", { path, content }, ...),
tool("list_files", "List directory contents", { path: z.string() }, ...),
tool("search_code", "Search for patterns", { pattern: z.string() }, ...),
// APPROVAL WORKFLOW
tool("apply_pending", "Apply approved changes", {}, ...),
tool("get_pending", "Show pending changes", {}, ...),
tool("clear_pending", "Discard pending changes", {}, ...),
// RESTART
tool("restart", "Rebuild and restart", {}, ...),
tool("health_check", "Check if bot is healthy", {}, ...),
],
});
const gitMcpServer = createSdkMcpServer({
name: "git",
version: "1.0.0",
tools: [
// STATUS
tool("status", "Show git status", {}, ...),
tool("diff", "Show changes", { path: z.string().optional() }, ...),
tool("log", "Show history", { count: z.number() }, ...),
// COMMIT & PUSH
tool("commit_code", "Commit code changes", { message: z.string() }, ...),
tool("git_push", "Push to GitHub", { branch: z.string().optional() }, ...),
// SYNC
tool("pull", "Pull from upstream", { source: z.enum(["main", "instance"]) }, ...),
tool("self_deploy", "Pull, build, restart", { source: z.enum(["main", "instance"]) }, ...),
// SAFETY
tool("rollback", "Revert commits", { commits: z.number() }, ...),
tool("health_check", "Detailed health report", {}, ...),
],
});
Self-Modification Checklist¶
Before enabling self-modification: - [ ] Git-based version control set up - [ ] Approval gates for code changes - [ ] Build verification before restart - [ ] Rollback mechanism available - [ ] Health check endpoint - [ ] Instance identity configured
When implementing: - [ ] Agent can read all project files - [ ] Agent can write files (with appropriate approval) - [ ] Agent can commit and push - [ ] Agent can pull updates - [ ] Agent can restart itself - [ ] Agent can roll back if needed