- Consolidated PG migration 054: agent_hooks rename + junction table + deprecated column drop - Updated SQLite schema v19 with final consolidated migration - Removed obsolete schema rebuild files (v21, v23) - Updated Go store layer (pg/hooks.go, sqlitestore/hooks.go) - Updated integration tests to use new table names (hooks, hook_agents) - Updated TypeScript protocol, UI components, and i18n strings - Updated gateway methods to reflect schema changes Tables: agent_hooks → hooks, agent_hook_agents → hook_agents
Builtin Hooks
Canonical, embed-shipped hook rows. Users may toggle enabled but cannot
edit content — the embedded .js is overwritten on every boot.
Adding a new builtin
-
Pick a stable kebab identifier (
pii-redactor,sql-guard). Never rename: the DB primary key isUUIDv5(namespace, id + "/" + event)and renaming would orphan any existing rows. -
Drop
my-feature.jsalongsidebuiltins.yaml. The script sees the goja sandboxed globalseventandresult, plus the usualJSON.*,Math.*. No network, no I/O, no timers. -
Add an entry to
builtins.yaml:- id: my-feature version: 1 events: [pre_tool_use] scope: global timeout_ms: 200 on_timeout: allow priority: 500 mutable_fields: [toolInput.command] source_file: my-feature.js description: "One-line user-facing summary." -
Bump
versionwhenever the JS content changes. On next boot, matching rows get UPDATEd in-place (the user'senabledtoggle is preserved). -
Remove
_placeholder.jsonce this is the first real.jsin the package.
Operator escape hatch
Operators can force-disable any builtin by id via hooks.builtin_disable in
config.json — useful when a builtin misbehaves before the next release:
{
"hooks": {
"builtin_disable": ["pii-redactor"]
}
}
The row stays in the DB (history preserved); only enabled=false is applied.
Version reconciliation rules
| Embed version | DB version | Outcome |
|---|---|---|
| missing row | — | INSERT, enabled=true |
| newer | older | UPDATE content; keep user's enabled toggle |
| equal | equal | no-op |
| older | newer | WARN log; keep DB (no rollback) |
A downgrade happens when someone rolls back the binary but the DB has already been written by a newer version. We never destructively rewrite; operators must resolve manually.