KeeperHub / KeeperHub/keeperhub

feat(plugins): add a typed OpenClaw agent-hook integration

Open
#2,343 1 comment 0 reactions 0 assignees View on GitHub
accepted enhancement
Dominant language
TypeScript
Stars
24
Forks
93
Avg merge
1d 8h
Merged PRs (30d)
266

Description

### Before filing

- [x] I searched open and closed issues and PRs for `OpenClaw`,
`/hooks/agent`, and agent webhooks.
- [x] I checked the docs and current behaviour on `staging` at
`e089f84356c5d31322b3a89299fe97a9c76ffd4c`.
- [x] This is one change, not several.

### Reason: what you cannot do today

I want a KeeperHub workflow to hand a structured onchain event or execution
result to an OpenClaw agent turn. OpenClaw exposes one stable ingress contract
for this: `POST /hooks/agent`, authenticated by a dedicated hook bearer token.
It accepts a required message plus optional agent, label, isolation, timeout,
delivery, and idempotency fields, and returns `200 { ok: true, runId }` after
the run is admitted.

KeeperHub can technically call it through the generic Webhook action, but that
is not an integration. The user has to put a long-lived hook token into the
action's free-form headers, hand-author the JSON payload, and remember several
non-obvious contract rules:

- the token must be in `Authorization: Bearer` or `x-openclaw-token`; query
tokens are rejected;
- it should be distinct from the Gateway authentication secret;
- `sessionMode: "isolated"` is the safe default;
- direct delivery requires both `channel` and `to`;
- the returned `runId` proves admission, not model completion or delivery;
- retries need the same idempotency key or they can create duplicate turns.

The generic Webhook plugin declares no credentials and stores its headers as
action configuration. KeeperHub's plugin credential path exists specifically
to keep credentials out of step parameters and observability. There is no
typed action today that uses that path for OpenClaw.

### Reason: what the workaround costs

Every workflow has to recreate authentication headers and a JSON envelope by
hand. A copied workflow also carries the secret-bearing header configuration
instead of referencing an organization-authorized integration. There is no
typed output telling the next node that `runId` means admission only, and the
generic action cannot prevent a user from enabling delivery with only half of a
destination.

The reliability cost is larger than the number of fields suggests: a workflow
retry can duplicate an agent turn, while a `200` can be misread as proof that
the requested work finished. Those are properties a connector can encode once
instead of leaving each workflow author to rediscover them.

### Scope: what this touches, and what it does not

**Touches:** one new `plugins/openclaw/` integration with one action,
`trigger-agent`. It stores a public OpenClaw hook base URL and dedicated hook
token as integration credentials, POSTs only to the fixed `/hooks/agent` path,
and returns the admission `runId` with an explicit `admitted: true` result.

**Deliberately does not touch:** existing Webhook behaviour, Gateway setup,
token provisioning or rotation, direct channel delivery, persistent-session
configuration, model/tool policy, completion polling, wallets, pricing,
database schema, or any existing plugin. A future wake-hook action could ship
independently and is not part of this issue.

The action defaults to `deliver: false` and `sessionMode: "isolated"`. It does
not expose `channel`, `to`, or `sessionKey` in v1, so it cannot accidentally
construct a partial direct-delivery or persistent-session request.

This is one change: a credential-backed typed wrapper around one existing
OpenClaw endpoint.

### Plan: what you propose

Add an `openclaw` plugin following `plugins/AGENTS.md`:

1. Integration fields:
- public instance URL (`type: "url"`, `OPENCLAW_BASE_URL`)
- dedicated hook token (`type: "password"`, `OPENCLAW_HOOK_TOKEN`)
2. One `trigger-agent` action with:
- required templated `message`
- optional `agentId`, `name`, `timeoutSeconds`, and `idempotencyKey`
- `deliver` fixed to `false` and `sessionMode` fixed to `isolated` in v1
3. The step fetches credentials at runtime, normalizes the configured base URL,
appends the fixed `/hooks/agent` path, and calls it through `safeFetch` with
`egress: "user-destination"` attribution.
4. Set `maxRetries = 0`. If an idempotency key is supplied, send it in the
`Idempotency-Key` header and body; the action itself still does not retry an
admitted turn.
5. Accept only HTTP 200 plus JSON `ok: true` and a nonempty `runId`. Return
`{ success: true, admitted: true, runId }`; never describe this as completed
or delivered. Bound error text and do not return or log credentials.
6. Add focused mocked tests for URL/path normalization, auth and idempotency
headers, fixed safe defaults, optional fields, malformed success payloads,
HTTP errors, network errors, and absence of token leakage.
7. Document that connection setup can validate field presence and URL shape
without dispatching a test agent turn; real credential validation happens
when the user intentionally runs the action.

No SDK dependency is needed.

### Plan: alternatives you considered

- **Keep using the generic Webhook plugin.** It reaches the endpoint but leaves
secrets in free-form action configuration and cannot encode admission,
idempotency, or routing semantics.
- **Expose all OpenClaw hook fields immediately.** Rejected for v1: direct
destinations and persistent session keys add coupled policy requirements and
can be added independently later.
- **Poll for completion.** Rejected: the webhook contract only guarantees
admission, and `runId` is not a general task/result API.
- **Hard-code OpenClaw Cloud.** Rejected: OpenClaw is commonly self-hosted, so
the destination must be user-configured and SSRF-guarded.

### Scope: compatibility

- [ ] Changes an existing response shape, status code, CLI flag, or default.
- [ ] Adds, removes, or upgrades a dependency.
- [ ] Changes database schema or requires a migration.
- [x] Touches authentication, permissions, validation, or spend limits.
- [ ] Changes pricing, plan limits, or anything a user is charged.

This proposal was prepared with AI assistance. No implementation has started;
I will wait for the `accepted` label and any maintainer changes to the plan.

Contributor guide

Open the contributing guide

Research direction

Start with plugins/AGENTS.md and inspect the existing plugin credential path and safeFetch entry point before adding plugins/openclaw/. Verify the trigger-agent action against the listed mocked-test cases, including fixed defaults, credential handling, response validation, and error paths. Done means a credential-backed POST to /hooks/agent returns only an admitted runId without leaking tokens or claiming completion.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, authentication, backend, testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.