First real builtin under Phase 04 infrastructure. Registers two DB rows
(user_prompt_submit + pre_tool_use) on startup, runs the embedded JS with
source-tier gate + dispatcher dotted-path allowlist.
- internal/hooks/builtin/pii-redactor.js (ES5.1, goja-compatible):
handle(event) returns updatedInput only when email or E.164-ish phone
actually matched. Redacts rawInput + toolInput.{command,query,content}.
- builtins.yaml: pii-redactor entry (version 1, priority 900, timeout 2s,
on_timeout=allow — missing redaction < blocking conversation).
- Drop _placeholder.js; pii-redactor.js is now the first real //go:embed match.
- Export builtin.Source(name) so handler tests load embedded JS without
going through Load()/cache state.
- Tests:
* loader_test: registry contains pii-redactor after Load().
* script_pii_redactor_test (handler-level, real goja runtime): email
masked in rawInput; +E164 phone masked; toolInput.command redacted,
toolInput.path left alone (not in mutable_fields); no PII → no
mutation; redact(redact(s)) == redact(s).
* dispatcher_test: 3 dotted-path walker cases (toolInput.command only
lets that key through; rawInput-only doesn't let forged toolInput
override original event keys; toolInput wildcard merges all keys).
Bench: ~242µs/op on 1 KiB Apple M4 Max, well under <1ms target.