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
49 changes: 1 addition & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Channel adapters that connect [World2Agent](https://github.com/machinepulse-ai/w
| --- | --- | --- |
| [`claude-code-channel`](./claude-code-channel) | [Claude Code](https://docs.claude.com/en/docs/claude-code) | MCP channel adapter + Claude Code plugin bundle. Signals arrive as in-session MCP notifications. |
| [`hermes-sensor-bridge`](./hermes-sensor-bridge) | [Hermes Agent](https://hermes-agent.nousresearch.com/) | Out-of-process supervisor + webhook bridge. Each signal triggers a fresh `AIAgent.run_conversation()` with the generated handler skill auto-loaded. |
| [`openclaw-plugin`](./openclaw-plugin) | [OpenClaw](https://docs.openclaw.ai/) | Native OpenClaw plugin. Conversational install via chat (Q&A driven by the sensor's `SETUP.md`), in-process polling, dispatch via OpenClaw's embedded-agent runtime into a per-sensor session lane keyed `agent:main:w2a-<sensor>` (main chat untouched). |

---

Expand Down Expand Up @@ -52,29 +51,6 @@ Each signal triggers a fresh agent run against the generated handler skill. See

---

## Quick start — OpenClaw

Install the plugin (`--dangerously-force-unsafe-install` is required because the plugin uses `child_process` to npm-install sensor packages on demand — OpenClaw's security scan blocks it otherwise):

```bash
openclaw plugins install @world2agent/openclaw-plugin --dangerously-force-unsafe-install
openclaw gateway restart
```

Then in a chat session with your `main` agent, just describe what you want to subscribe to:

```
> subscribe me to Hacker News — I care about AI and security stories
```

The bundled `world2agent-manage` skill takes over: reads the sensor's `SETUP.md`, asks you 1–3 questions to personalize the handler, writes both the config and the personalized SKILL.md, registers the sensor, and reloads the running plugin — without any manual CLI work.

> If reload returns `ok: false` (for example a gateway-call timeout), the agent will ask **you** to run `openclaw gateway restart` in your own terminal. It intentionally doesn't run that command itself — restarting the gateway from inside the chat would kill the gateway process and truncate the agent's reply mid-sentence. After reload or restart, the sensor starts polling within seconds; subsequent polls follow the sensor's configured `interval_seconds`.

Signals route to a per-sensor session lane (`agent:main:w2a-<sensor>`) — your `main` chat is untouched. Open the `w2a-<sensor>` lane in the OpenClaw dashboard (<http://127.0.0.1:18789/>) to see how the agent reacts to each signal. See [`openclaw-plugin/README.md`](./openclaw-plugin/README.md) for the full install reference, dispatcher internals, and CLI fallback.

---

## Repository layout

```
Expand All @@ -87,7 +63,7 @@ Signals route to a per-sensor session lane (`agent:main:w2a-<sensor>`) — your
│ ├── skills/ # MCP-side handler skills
│ ├── src/
│ └── package.json
── hermes-sensor-bridge/ # @world2agent/hermes-sensor-bridge
── hermes-sensor-bridge/ # @world2agent/hermes-sensor-bridge
│ ├── src/
│ │ ├── runner/ # per-sensor subprocess
│ │ └── supervisor/ # daemon (signal → HMAC → POST → Hermes)
Expand All @@ -96,16 +72,6 @@ Signals route to a per-sensor session lane (`agent:main:w2a-<sensor>`) — your
│ │ └── scripts/ # all host-side work (install, remove, list, …)
│ ├── e2e/
│ └── package.json
└── openclaw-plugin/ # @world2agent/openclaw-plugin
├── src/
│ ├── dispatch.ts # runEmbeddedAgent + `# System Event` framing
│ ├── runtime.ts # in-process sensor lifecycle
│ ├── isolated.ts # opt-in subprocess mode (reuses Hermes runner)
│ └── cli.ts # `openclaw world2agent sensor add | list | remove`
├── skills/world2agent-manage/
│ └── SKILL.md # conversational install + management skill
├── test/
└── package.json
```

---
Expand All @@ -127,19 +93,6 @@ Users pull updates with:

Bump `version` in `hermes-sensor-bridge/package.json`, then `pnpm publish --access public --tag alpha` (alpha) or `latest` (stable). Users pull the runtime with `npm install -g @world2agent/hermes-sensor-bridge@<tag>`. The skill is installed separately via `hermes skills install …`; re-run that command with `--force` to refresh to the latest skill content.

### OpenClaw plugin (`openclaw-plugin`)

Bump `version` in `openclaw-plugin/package.json`, then `pnpm publish --access public --tag alpha` (alpha) or `latest` (stable). Users pull updates with:

```bash
openclaw plugins install @world2agent/openclaw-plugin@<tag> --dangerously-force-unsafe-install
openclaw gateway restart
```

The bundled `world2agent-manage` skill ships inside the package, so it updates atomically with the plugin — no separate install step.

---

## License

Apache-2.0
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Verify it loaded:

```bash
openclaw plugins list | grep world2agent
# → │ World2Agent │ world2agent │ openclaw │ enabled │ ... │ 0.1.0-alpha.0
# → │ World2Agent │ world2agent │ openclaw │ enabled │ ... │ 0.1.0 │
openclaw world2agent --help
# → Commands: reload, sensor
```
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "@world2agent/openclaw-plugin",
"version": "0.1.0-alpha.0",
"version": "0.1.0",
"description": "World2Agent native plugin for OpenClaw",
"license": "Apache-2.0",
"author": "MachinePulse Pte. Ltd.",
"homepage": "https://github.com/machinepulse-ai/world2agent",
"repository": {
"type": "git",
"url": "git+https://github.com/machinepulse-ai/world2agent-plugins.git",
"directory": "openclaw-plugin"
"directory": "openclaw-plugin-wip"
},
"bugs": {
"url": "https://github.com/machinepulse-ai/world2agent-plugins/issues"
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ and copy mode):
PLUGIN_DIR=$(openclaw plugins list --json | \
jq -r '.plugins[] | select(.id == "world2agent") | .rootDir')
echo "$PLUGIN_DIR"
# → e.g. /Users/<you>/Documents/.../openclaw-plugin
# → e.g. /Users/<you>/Documents/.../openclaw-plugin-wip
```

Then check whether the sensor package is already installed there:
Expand Down
3 changes: 0 additions & 3 deletions openclaw-plugin/src/cli.ts → openclaw-plugin-wip/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { join } from "node:path";
import { packageToSkillId } from "@world2agent/sdk";
import {
assertContextInjectionCompatible,
loadEffectiveOpenClawConfig,
normalizeDeliver,
upsertDedicatedAgentSkillAllowlist,
Expand Down Expand Up @@ -117,7 +116,6 @@ async function runAddCommand(
options: Record<string, unknown>,
): Promise<unknown> {
const config = await loadEffectiveOpenClawConfig(services.api);
assertContextInjectionCompatible(config);

const installed = await ensurePackageInstalled(pkg);
const sensorId = optionString(options, "sensorId") ?? defaultSensorId(pkg);
Expand Down Expand Up @@ -290,4 +288,3 @@ function optionString(options: Record<string, unknown>, key: string): string | u
function optionBoolean(options: Record<string, unknown>, key: string): boolean {
return options[key] === true;
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import type {
} from "./openclaw/plugin-sdk/types.js";
import type { DeliverConfig, RequiredWorld2AgentPluginConfig } from "./types.js";

export const REQUIRED_CONTEXT_INJECTION = "continuation-skip";

export async function loadEffectiveOpenClawConfig(
api: OpenClawPluginApi,
): Promise<OpenClawConfig> {
Expand All @@ -23,17 +21,6 @@ export async function loadEffectiveOpenClawConfig(
return api.config ?? {};
}

export function assertContextInjectionCompatible(config: OpenClawConfig): void {
const got = config.agents?.defaults?.contextInjection;
if (got === REQUIRED_CONTEXT_INJECTION) return;

throw new Error(
"OpenClaw config field `agents.defaults.contextInjection` must be set to " +
`"${REQUIRED_CONTEXT_INJECTION}" for @world2agent/openclaw-plugin. ` +
`Current value: ${JSON.stringify(got)}. Update that exact field and retry.`,
);
}

export function normalizePluginConfig(
value: unknown,
): RequiredWorld2AgentPluginConfig {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { EmbeddedDispatcher, HttpDispatcher } from "./dispatch.js";
import {
assertContextInjectionCompatible,
loadEffectiveOpenClawConfig,
normalizePluginConfig,
} from "./config.js";
Expand Down Expand Up @@ -48,7 +47,6 @@ export function createWorld2AgentPlugin() {
}

const openclawConfig = api.config ?? {};
assertContextInjectionCompatible(openclawConfig);
const openclawConfigRef = { current: openclawConfig };

// If a previous register() left a runtime running in this same
Expand Down Expand Up @@ -96,7 +94,6 @@ export function createWorld2AgentPlugin() {

api.registerGatewayMethod?.("world2agent.reload", async () => {
const nextConfig = await loadEffectiveOpenClawConfig(api);
assertContextInjectionCompatible(nextConfig);
openclawConfigRef.current = nextConfig;
const manifest = await readManifest(paths);
return {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ describe("contextInjection startup check", () => {
process.env.OPENCLAW_HOME = ORIGINAL_OPENCLAW_HOME;
});

it("fails register() synchronously when agents.defaults.contextInjection is not continuation-skip", async () => {
it("does not fail register() when agents.defaults.contextInjection is always", async () => {
const root = await mkdtemp(join(tmpdir(), "w2a-openclaw-register-"));
process.env.W2A_HOME = join(root, "w2a");
process.env.OPENCLAW_HOME = join(root, "openclaw");

const plugin = createWorld2AgentPlugin();
expect(() =>
expect(() => {
plugin.register({
config: {
agents: {
Expand All @@ -29,10 +29,8 @@ describe("contextInjection startup check", () => {
},
},
pluginConfig: {},
}),
).toThrow(
"OpenClaw config field `agents.defaults.contextInjection` must be set to \"continuation-skip\"",
);
});
}).not.toThrow();
});

it("register() returns synchronously (not a promise) — OpenClaw drops async registers", async () => {
Expand All @@ -49,4 +47,3 @@ describe("contextInjection startup check", () => {
expect(result).toBeUndefined();
});
});

File renamed without changes.
File renamed without changes.
Loading