Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions docs/design/opencoat-openclaw-joinpoint-model-v0.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,8 @@ error.detected
| MVP joinpoint | Emitted today | Bridge source | Sync veto at host call site |
| --- | --- | --- | --- |
| `input.received` | yes | `message_received`, `inbound_claim`, `before_dispatch` | no (observe / buffer) |
| `queue.before_enqueue` | yes (observe) | queue depth poll (`getFollowupQueueDepth`) | no — needs native hook at `enqueueFollowupRun` |
| `queue.before_enqueue` | yes | **`queue_before_enqueue` plugin hook** (fork); queue depth poll fallback | **yes** — `block` / `queue.prompt` / `queue.summary_line` rewrite via bridge `queue_guard` |
| `queue.after_enqueue` | yes (observe) | **`queue_after_enqueue` plugin hook** (fork); poll snapshot sync | no |
| `queue.before_collect` | yes (observe) | queue depth poll (depth decrease) | no |
| `reply_run.before_begin` | yes (observe) | `onAgentEvent` lifecycle `start` | no |
| `reply_run.phase.running` | yes (observe) | first `assistant` / `tool` / `item` after start | no |
Expand All @@ -391,7 +392,7 @@ error.detected
| `response.before_final` | partial | `message_sending` cancel path | cancel outbound only |
| `verification.after_fail` | no | — | — |
| `heartbeat.before_run` | no | OpenCOAT `runtime_tick` / future hook | — |
| `error.detected` | partial | `on_error` lifecycle alias via agent error events | no |
| `error.detected` | yes (observe) | `onAgentEvent` lifecycle `error` | no |

Default: `runtimeObservers: true`, `observerPollMs: 500`. Full hook table: [bridge README](../../integrations/openclaw-opencoat-bridge/README.md).

Expand All @@ -413,7 +414,7 @@ C. Not direct — needs OpenClaw middleware/hook PR, or is OpenCOAT-internal

**Weakest / internal-only:** `token.*`, `span.*`, `prompt.section.*` (until host passes structured sections), implicit planner steps, true memory read/write on every path, unified `response.before_final` verifier (partial via `message_sending` only).

**Strong control gap (upstream):** synchronous veto at `enqueueFollowupRun`, full `reply_run.phase.*`, and any path where only post-hoc agent events exist today.
**Strong control gap (upstream):** full `reply_run.phase.*`, `tool.result.before_emit`, unified `memory.before_write`, and paths where only post-hoc agent events exist today. **`queue.before_enqueue` sync veto shipped on OpenClaw fork** (`queue_before_enqueue` hook + bridge `queue_guard`); poll remains observe-only fallback.

### 5.2 Tier A — usable now

Expand All @@ -429,19 +430,20 @@ C. Not direct — needs OpenClaw middleware/hook PR, or is OpenCOAT-internal
| Agent event stream | `stream: compaction` start/end | memory JPs | observer | observe |
| Agent event stream | `stream: lifecycle` start | `reply_run.before_begin` | observer | observe |
| Agent event stream | `stream: tool` / `item` / `assistant` after start | `reply_run.phase.running` | observer | observe |
| Agent event stream | `stream: command_output` | `command.output_stream` | not wired | observe (future) |
| Agent event stream | `stream: patch` | `patch.summary_created` | not wired | observe (future) |
| Agent event stream | `stream: command_output` | `command.output_stream` | yes (observe) | no |
| Agent event stream | `stream: patch` | `patch.summary_created` | yes (observe) | no |
| Task API | `runtime.tasks.runs.bindSession().list()` | `task.*` | poll diff | observe |
| Task hooks | `subagent_*` | `task.after_create`, `task.before_terminal` | yes | observe / spawn veto |
| Input | `message_received`, `inbound_claim`, `before_dispatch` | `input.received` | yes | observe / extract |
| Plugin hooks | `queue_before_enqueue` / `queue_after_enqueue` | `queue.before_enqueue` / `queue.after_enqueue` | yes (fork) | **block** / prompt & summaryLine **rewrite** |
| Queue poll | `getFollowupQueueDepth` | `queue.before_enqueue`, `queue.before_collect` | fallback | observe only (no veto) |

**Important correction:** OpenClaw’s plugin hook `before_tool_call` **is** a pre-execute guard — do not confuse it with `onAgentEvent` `stream: tool`, which fires around tool **start** and is observe-only. Catalog name `tool.before_call` maps to the plugin hook, not `tool.started`.

### 5.3 Tier B — wrapper or weak modulation

| Joinpoint (design) | How to attach | Bridge / OpenCOAT today |
| --- | --- | --- |
| `queue.before_enqueue` / `after_enqueue` | wrap `enqueueFollowupRun` or depth poll | poll only (late) |
| `queue.before_drain` | wrap `scheduleFollowupDrain` | not wired |
| `input.before_enqueue` | adapter before enqueue | partial (`on_user_input` buffer) |
| `prompt.before_send_to_model` | `FollowupRun.extraSystemPrompt`, hook fold | plugin + discovery |
Expand All @@ -458,7 +460,6 @@ OpenCOAT applies **weak modulation** here: extra system prompt, queue/task *poli

```text
tool.before_execute # if stricter than plugin before_tool_call (args rewrite mid-flight)
queue.before_enqueue # sync veto at enqueueFollowupRun (bridge poll is too late)
reply_run.phase.* # per-phase hooks on ReplyOperation
memory.before_read / before_write # unified memory middleware (compaction hooks are partial)
response.before_final # unified verifier before channel delivery (message_sending is partial)
Expand Down Expand Up @@ -498,11 +499,13 @@ Upstream “neurosurgery” (recommended order):
| Wave | Joinpoints | Mechanism |
| --- | --- | --- |
| **Shipped (bridge)** | §4.1 MVP rows marked “yes” | plugin hooks + `runtime-observers.ts` |
| **Next (observe)** | `command.output_stream`, `patch.summary_created`, streaming deltas | extend `onAgentEvent` mapping in bridge (use catalog names, not `command.output`) |
| **Next (upstream)** | sync `queue.before_enqueue`, `reply_run.phase.*`, `response.before_final` | OpenClaw plugin hooks at call sites |
| **Shipped (observe)** | `command.output_stream`, `patch.summary_created`, `error.detected` | `onAgentEvent` mapping in bridge `runtime-observers.ts` |
| **Next (observe)** | streaming deltas | extend `onAgentEvent` / outbound callbacks |
| **Shipped (fork + bridge)** | `queue.before_enqueue` / `queue.after_enqueue` sync veto + observe | OpenClaw `queue_before_enqueue` / `queue_after_enqueue` + bridge `queue_guard` |
| **Next (upstream)** | `reply_run.phase.*`, `response.before_final`, `tool.result.before_emit` | OpenClaw plugin hooks at call sites |
| **OpenCOAT-only** | `span.*`, `token.*`, message children | discovery on prompt payload |

Design catalog lists **17 MVP names**; bridge **strong loop** today is smaller: input → prompt fold → tool guard → optional outbound cancel → task/subagent edges, plus observe-only queue/run/task/event stream for DCN.
Design catalog lists **17 MVP names**; bridge **strong loop** today: input → prompt fold → tool guard → optional outbound cancel → **queue enqueue veto/rewrite (fork)** → task/subagent edges, plus observe-only run/task/event stream for DCN. Dogfood: [`examples/09_queue_hook_dogfood`](../../examples/09_queue_hook_dogfood/README.md).

---

Expand Down Expand Up @@ -656,7 +659,7 @@ Bridge module `runtime-observers.ts` — uses host APIs already available to plu

| Source | Joinpoints | Mechanism |
| --- | --- | --- |
| `api.runtime.events.onAgentEvent` | `reply_run.before_begin`, `reply_run.phase.running`, `planning.plan_updated`, `approval.requested`, compaction → `before_memory_write` / `after_memory_write` | event stream |
| `api.runtime.events.onAgentEvent` | `reply_run.before_begin`, `reply_run.phase.running`, `planning.plan_updated`, `approval.requested`, `command.output_stream`, `patch.summary_created`, `error.detected` (lifecycle `error`), compaction → `before_memory_write` / `after_memory_write` | event stream |
| `api.registerHook` | `session:compact:before` / `after` → memory JPs | internal gateway hooks |
| Host `getFollowupQueueDepth` | `queue.before_enqueue`, `queue.before_collect` | `registerService` poll per tracked `sessionKey` |
| `api.runtime.tasks.runs.bindSession().list()` | `task.before_create`, `task.after_create`, `task.before_terminal` | task registry diff poll |
Expand Down
57 changes: 55 additions & 2 deletions docs/guides/concern-authoring-aop.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,22 @@ legacy `pointcut` / `advice` / `weaving_policy` are optional and sync automatica

## Message-level guard (OpenClaw bridge)

The gateway bridge registers **26** plugin hooks plus **runtime observers** (`onAgentEvent`,
queue/task poll) for MVP joinpoints such as `queue.before_enqueue` and `reply_run.before_begin`
The gateway bridge registers **29** plugin hooks plus **runtime observers** (`onAgentEvent`,
queue/task poll) for MVP joinpoints such as `queue.before_enqueue` (sync **block/rewrite**
on OpenClaw fork via `queue_before_enqueue`) and `reply_run.before_begin`
(observe-only — see [joinpoint model §4.1](../design/opencoat-openclaw-joinpoint-model-v0.1.md#41-mvp-emit-status-bridge-integrationsopenclaw-opencoat-bridge)).
Dogfood concerns: [`examples/09_queue_hook_dogfood`](../examples/09_queue_hook_dogfood/README.md).

### Decision vs observe (OpenClaw fork + bridge)

Use the **HyperdustLabs fork** (`opencoat/hooks-v0.1`, see [openclaw-fork-dev.md](openclaw-fork-dev.md)) — not npm registry OpenClaw — for native queue hooks and dogfood.

| Class | Joinpoints (examples) | Host effect today |
| --- | --- | --- |
| **Decision** | `queue.before_enqueue`, `tool.before_call`, `subagent_spawning` → `task.before_create` | block, rewrite, spawn veto, prompt prepend, outbound cancel |
| **Observe** | `reply_run.*`, `planning.*`, `approval.requested`, `command.output_stream`, `patch.summary_created`, `error.detected`, queue poll fallback | DCN / activation only; no sync veto |

**Next decision hooks** ship on the **same fork branch** (`tool_result_persist`, `reply_run.phase.*`, `response.before_final`, …) — not upstream `openclaw/openclaw`. See [fork hook backlog](openclaw-fork-dev.md#fork-hook-backlog-post-queue).

Prefer `user_message()` over flat `before_response` when the bridge sends `messages[]`:

Expand All @@ -62,6 +75,46 @@ Prefer `user_message()` over flat `before_response` when the bridge sends `messa
}
```

## Queue guard (OpenClaw fork + bridge)

Target `queue.before_enqueue` with explicit `joinpoints` (dotted names are not parsed
from `expression()` today). Bridge maps woven advice to OpenClaw `queue_before_enqueue`:

| `effect.target` | `effect.mode` | OpenClaw result |
| --- | --- | --- |
| `queue.prompt` | `block` | skip enqueue |
| `queue.prompt` | `rewrite` | replace queued prompt |
| `queue.summary_line` | `rewrite` | replace summary line |

```json
{
"id": "oc.dogfood.queue-block",
"pointcuts": [
{
"id": "pc-queue",
"joinpoints": ["queue.before_enqueue"],
"match": { "any_keywords": ["QUEUE_DOGFOOD_BLOCK"] }
}
],
"advices": [
{
"kind": "before",
"pointcut_ref": "pc-queue",
"template": "memory_write_guard",
"content": "Follow-up queue blocked by policy.",
"effect": {
"mode": "block",
"level": "memory_level",
"target": "queue.prompt",
"priority": 0.95
}
}
]
}
```

Full dogfood set: [`examples/09_queue_hook_dogfood`](../examples/09_queue_hook_dogfood/README.md).

## Declare precedence

```json
Expand Down
94 changes: 94 additions & 0 deletions docs/guides/openclaw-fork-dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# OpenClaw fork development (1:1 with global CLI)

OpenCOAT + OpenClaw bridge development uses the **HyperdustLabs fork**, not npm
registry OpenClaw or the upstream `openclaw/openclaw` main tree.

| Item | Canonical path |
| --- | --- |
| Fork repo | `~/openclaw-fork` |
| Remote | `https://github.com/HyperdustLabs/openclaw.git` |
| Branch | `opencoat/hooks-v0.1` |
| Lock file | `~/.openclaw/openclaw-fork.json` |

## Policy

1. **Global `openclaw` must be 1:1 with `~/openclaw-fork`** — same commit, same
`openclaw.mjs`, same `dist/index.js`. No separate npm registry install.
2. **Gateway LaunchAgent** must run `~/openclaw-fork/dist/index.js` (port 18789).
3. **Do not use** `~/openclaw` (upstream clone) for OpenCOAT dogfood — it lacks
queue hooks and fork-specific plugin SDK surfaces.
4. After every `git pull` in the fork, re-run bind + rebuild.

## One-time setup

From OpenCOAT repo root:

```bash
./scripts/use-openclaw-fork.sh --clone # if ~/openclaw-fork missing
# or, if fork already exists:
./scripts/use-openclaw-fork.sh
```

This:

- `npm install -g .` from `~/openclaw-fork` (symlink, not registry copy)
- installs `~/.local/bin/openclaw` → `openclaw-fork` shim
- reinstalls LaunchAgent gateway on fork `dist/`
- writes `~/.openclaw/openclaw-fork.json`

## Daily workflow

```bash
cd ~/openclaw-fork
git pull origin opencoat/hooks-v0.1
# ... edit, commit, push to HyperdustLabs/openclaw ...

cd /path/to/OpenCOAT
./scripts/use-openclaw-fork.sh --update # pull + pnpm install + build + rebind
./scripts/check-openclaw-fork.sh # verify 1:1
```

Bridge rebuild after TS changes:

```bash
cd integrations/openclaw-opencoat-bridge && npm run build
openclaw daemon restart
```

## Verify

```bash
./scripts/check-openclaw-fork.sh
openclaw --version # e.g. 2026.5.19 (593c5de)
openclaw gateway status # CLI version == Gateway version
# Command line should include: ~/openclaw-fork/dist/index.js
```

## What breaks 1:1 alignment

| Action | Fix |
| --- | --- |
| `npm install -g openclaw@latest` (registry) | `./scripts/use-openclaw-fork.sh` |
| Running gateway from `/tmp/...` without updating LaunchAgent | `openclaw gateway install --force` |
| Fork pulled but not rebuilt | `cd ~/openclaw-fork && pnpm build` then `./scripts/use-openclaw-fork.sh` |
| Old BAIclaw shim on PATH | removed by `use-openclaw-fork.sh`; use fork shim only |

## Fork hook backlog (post-queue)

After [PR #77](https://github.com/HyperdustLabs/OpenCOAT/pull/77) (queue `queue_before_enqueue` / `queue_after_enqueue` + bridge `queue_guard`) lands on `main`, plan **paired fork + OpenCOAT PRs** on `opencoat/hooks-v0.1`:

| Priority | Fork hook / joinpoint | Notes |
| --- | --- | --- |
| 1 | `tool_result_persist` | Fork hook is **sync-only** today; needs async or local policy cache before bridge can weave |
| 2 | `reply_run.phase.*` | Native hooks at `ReplyOperation` phase edges (not lifecycle approx) |
| 3 | `response.before_final` | Unified verifier before channel delivery (beyond `message_sending` cancel) |
| 4 | `memory.before_write` | Unified memory middleware (compaction hooks are observe-only today) |
| 5 | `queue.before_drain` | Wrap `scheduleFollowupDrain` |

Bridge skipped (fork has hook, hot path): `before_message_write`, `tool_result_persist` until sync/async contract is extended.

## Related

- [OpenClaw bridge README](../../integrations/openclaw-opencoat-bridge/README.md)
- [Joinpoint model §4.1 queue hooks](../design/opencoat-openclaw-joinpoint-model-v0.1.md)
- HyperdustLabs PR: `opencoat/hooks-v0.1` on `HyperdustLabs/openclaw`
Loading
Loading