mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 10:18:42 +00:00
feat(hooks/script): integrate script handler — FireResult + source-tier gate + migration
Wave 1 Phase 03. Wires the Phase 02 Goja handler into the dispatcher, widens
the DB schema for script + builtin sources, and refactors Dispatcher.Fire to
return a FireResult so builtin-source hooks can mutate event input.
Dispatcher:
- FireResult{Decision, UpdatedToolInput, UpdatedRawInput} replaces the plain
Decision return. stdDispatcher.runSync keeps a local evMut copy so a
builtin-source hook's updatedInput flows to downstream hooks in the chain
and to the caller; non-builtin (source=ui) script mutations are dropped +
WARN-logged (defense-in-depth; source tier enforced at the dispatcher, not
only at the handler).
- applyBuiltinMutation + placeholder builtinAllowlistFor (rawInput, toolInput)
— Phase 04 overrides the allowlist from builtins.yaml.
- noopDispatcher returns FireResult{DecisionAllow}.
- Script mutation tests: TestDispatcher_ScriptMutation_BuiltinSourceApplies,
TestDispatcher_ScriptMutation_UISourceDenied.
Pipeline + callers (14 Fire sites refactored):
- FireHook wrapper returns FireResult; context_stage.go:52 applies
UpdatedRawInput → state.Input.Message; tool_stage.go:52 applies
UpdatedToolInput → tc.Arguments before ExecuteToolCall.
- delegate_bridge + delegate_tool keep the Decision branch; Updated* ignored.
- dispatcher_test / delegate_bridge_test / delegate_tool_hooks_test /
integration test helpers updated for the new shape.
Validation:
- edition_gate allows HandlerScript on every edition (sandboxed, no shell
escape surface).
- config.validateHandler rejects empty source, source > 32 KiB, goja compile
error; validateTimeout rejects on_timeout=ask|defer (reserved).
- Six new config tests cover the script path.
Migrations:
- PG migration 000053 relaxes handler_type + source CHECKs and drops
uq_hooks_{global,tenant,agent} — scripts routinely want many small hooks
per event. RequiredSchemaVersion → 53.
- SQLite SchemaVersion → 21; patch 20 is a SELECT 1 placeholder because
SQLite can't ALTER a CHECK — rebuildAgentHooksV21 runs outside the
migration tx (parallel to backfillV16) to rename/recreate the table with
widened CHECKs. schema.sql fresh-DB path matches.
- H9 fix: PGHookStore.Create + SqliteHookStore.Create honor caller-provided
cfg.ID (uuid.Nil falls back to UUIDv7), unblocking Phase 04 idempotent
UUIDv5 seed. TestCreateHonorsFixedID on both stores.
Config:
- config.HooksConfig{ScriptConcurrency, ScriptPerTenantConcurrency,
ScriptCacheSize, BuiltinDisable}; buildHookHandlers wires the script
handler with the caps from appCfg.Hooks.
Gates green: go build ./... + sqliteonly, go test -race -count=1 across
hooks/pipeline/sqlitestore.
This commit is contained in:
1 parent
a556161d0d
commit
b7592bcacf
29 files changed
+663
-125
No files matched your search
@@ -1570,9 +1570,12 @@ CREATE INDEX IF NOT EXISTS idx_vault_links_source
|
||||
WHERE json_extract(metadata, '$.source') IS NOT NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- Table: agent_hooks (migration 000052)
|
||||
-- Table: agent_hooks (migrations 000052 + 000053)
|
||||
-- SQLite translation: JSONB→TEXT, TIMESTAMPTZ→TEXT, UUID→TEXT,
|
||||
-- BYTEA→BLOB, DATE→TEXT (ISO8601), CHECK for enums.
|
||||
-- v21 (migration 000053): handler_type adds 'script', source adds 'builtin',
|
||||
-- old (event, handler_type) uniqueness indexes dropped — scripts routinely
|
||||
-- want many small hooks per event.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agent_hooks (
|
||||
@@ -1581,7 +1584,7 @@ CREATE TABLE IF NOT EXISTS agent_hooks (
|
||||
agent_id TEXT REFERENCES agents(id) ON DELETE CASCADE,
|
||||
scope TEXT NOT NULL CHECK (scope IN ('global', 'tenant', 'agent')),
|
||||
event TEXT NOT NULL,
|
||||
handler_type TEXT NOT NULL CHECK (handler_type IN ('command', 'http', 'prompt')),
|
||||
handler_type TEXT NOT NULL CHECK (handler_type IN ('command', 'http', 'prompt', 'script')),
|
||||
config TEXT NOT NULL DEFAULT '{}',
|
||||
matcher TEXT,
|
||||
if_expr TEXT,
|
||||
@@ -1590,25 +1593,13 @@ CREATE TABLE IF NOT EXISTS agent_hooks (
|
||||
priority INTEGER NOT NULL DEFAULT 0,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
source TEXT NOT NULL DEFAULT 'ui' CHECK (source IN ('ui', 'api', 'seed')),
|
||||
source TEXT NOT NULL DEFAULT 'ui' CHECK (source IN ('ui', 'api', 'seed', 'builtin')),
|
||||
metadata TEXT NOT NULL DEFAULT '{}',
|
||||
created_by TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_hooks_global
|
||||
ON agent_hooks (event, handler_type)
|
||||
WHERE scope = 'global';
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_hooks_tenant
|
||||
ON agent_hooks (tenant_id, event, handler_type)
|
||||
WHERE scope = 'tenant';
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_hooks_agent
|
||||
ON agent_hooks (tenant_id, agent_id, event, handler_type)
|
||||
WHERE scope = 'agent';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_hooks_lookup
|
||||
ON agent_hooks (tenant_id, agent_id, event)
|
||||
WHERE enabled = 1;
|
||||
|
||||
Reference in new issue
Block a user