Skip to content

feat(agent-trigger): Webhook and Manual trigger backends (CodeDiscovery B-02) - #412

Draft
esafwan wants to merge 2 commits into
developfrom
task/CD-B-02-webhook-manual
Draft

feat(agent-trigger): Webhook and Manual trigger backends (CodeDiscovery B-02)#412
esafwan wants to merge 2 commits into
developfrom
task/CD-B-02-webhook-manual

Conversation

@esafwan

@esafwan esafwan commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Problem

CodeDiscovery finding B-02: Agent Trigger.trigger_type declares Webhook / App Event / Manual, but only Schedule and Doc Event have backend consumers — a user can save a Webhook or Manual trigger and nothing can ever fire it. (Decomposition dossier on file. App Event is deliberately excluded — design-first, two glossary terms unratified.)

Scope

  • New huf/ai/agent_trigger_api.py:
    • agent_trigger_webhook(slug, webhook_key) — guest endpoint: deterministic enabled-trigger lookup by slug (ambiguity-safe), constant-time fail-closed key check (hmac.compare_digest, empty configured key rejects), raw-body size cap BEFORE parsing, payload embedded as explicitly-labeled untrusted data in the prompt, enqueues run_agent_sync as the trigger owner (frappe.set_user, same pattern as flow_webhook) with channel_id="webhook".
    • run_trigger(trigger_name, prompt?) — authenticated Manual trigger runner: Agent Trigger read permission + agent.use capability, runs synchronously as the session user with channel_id="manual".
  • agent_trigger.py: validate() requires slug+key for Webhook triggers and enforces slug uniqueness across enabled webhook triggers; event_name gets its missing depends_on.

Key decisions

  • Webhook key is the auth boundary (fail closed); execution identity = trigger owner, never Guest.
  • Manual runs synchronously in-request (mirrors chat behavior; long agents block the response — documented).
  • Deferred per decomposition: W3 (webhook_key Data→Password migration), W5 (URL display in UI), W6 (tests — suite currently can't run on develop, see fix(tests): migrate off deprecated FrappeTestCase so the suite runs on Frappe 16 #407), all App Event work.

Agent contributions

Implementation by the release-orchestration pipeline; an independent second review REJECTED the first cut (Guest execution identity, nondeterministic slug lookup, post-parse payload cap); all three findings fixed by the pipeline; second commit.

Tests executed

  • py_compile + JSON schema validation.
  • Isolated bench 16_ro console smoke: owner set_user present, disabled-filter + raw cap present, key check accepts/rejects correctly, untrusted labeling present.

Risks and limitations

  • Guest-facing endpoint: the key is the only barrier — leaked key = run the agent as its owner (bounded by key secrecy; same trade-off as flow webhooks).
  • webhook_key is still a plain Data field (plaintext at rest) until W3 lands.
  • Not yet exercised with a live HTTP webhook round-trip (site-level smoke recommended before undrafting).

Rollback

Revert the 2 commits; Webhook/Manual types return to inert (current develop behavior).

Remaining work

esafwan added 2 commits July 20, 2026 08:15
- W1: new guest endpoint agent_trigger_webhook in huf/ai/agent_trigger_api.py
  resolving triggers by webhook_slug, rejecting disabled triggers, validating
  the key with hmac.compare_digest (fail closed), parsing JSON/form payload
  into a prompt, and enqueueing run_agent_sync (pattern copied from
  flow_api.py flow_webhook/_webhook_key_is_valid)
- W4: conversation identity channel_id='webhook'/'manual' with external_id
  per the agent's persist_user_history setting (agent_hooks.py pattern)
- M1: authenticated run_trigger(trigger_name) for Manual triggers with
  Agent Trigger read permission + agent.use capability check, running
  run_agent_sync(channel_id='manual') as the session user
- W2: AgentTrigger.validate() requires webhook_slug+webhook_key for Webhook
  triggers and enforces slug uniqueness among enabled webhook triggers;
  event_name now has depends_on for App Event in the DocType schema
…kup, raw body cap

- run as trigger owner via frappe.set_user (Guest would be rejected by
  run_agent_sync for non-guest agents); webhook key remains the auth boundary
- resolve slug with disabled=0 filter + ambiguity check (validation only
  enforces uniqueness across enabled triggers)
- hard-cap the raw request body before parsing; label payload as untrusted
  in the generated prompt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant