npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lessinbox/openclaw-channel

v0.2.11

Published

Openclaw channel plugin that sends and receives agent conversation state through Lessinbox.

Readme

@lessinbox/openclaw-channel

Openclaw channel plugin that sends and receives agent conversation state through Lessinbox.

What it does

  • Keeps one continuous conversation per target (channel:* or thread:*)
  • Starts new Lessinbox sessions (runs) only when needed
  • Posts assistant messages into Lessinbox threads
  • Maintains persistent conversationId -> { threadId, runId? } mapping (memory or redis)
  • Opens workspace WebSocket stream and emits realtime inbound events (message.created, interrupt.answered, run.state_changed, etc.)
  • Exposes a channel plugin manifest + Lessinbox skill bundle
  • Exposes guardrails middleware helpers for side-effect tooling (executeLessinboxGuardedExecution, executeLessinboxOpenclawMiddleware)

Package contents

  • dist/index.js plugin runtime entry
  • openclaw.plugin.json manifest
  • skills/lessinbox/SKILL.md

Required account config

{
  "channels": {
    "lessinbox": {
      "enabled": true,
      "toolHooks": {
        "enabled": false,
        "policyDryRunLogging": true,
        "persistExecutionArtifacts": true,
        "debugPersistence": false
      },
      "workItems": {
        "autoCreateOnNewConversation": false
      },
      "accounts": {
        "default": {
          "enabled": true,
          "apiUrl": "https://lessinbox.example.com",
          "apiKey": "<LESSINBOX_API_KEY>",
          "workspaceId": "wrk_xxx",
          "defaultChannelId": "chn_xxx",
          "runtimeProviderId": "openclaw",
          "inboundCommandAuthorized": false,
          "mappingStore": "redis",
          "redisUrl": "redis://localhost:6379",
          "redisPrefix": "lessinbox:openclaw",
          "workspaceStream": {
            "enabled": true,
            "wsUrl": "wss://lessinbox.example.com/v1/ws",
            "reconnectMs": 1500
          }
        }
      }
    }
  }
}

runtimeProviderId defaults to openclaw. Set it to the exact Lessinbox Skill Market provider id for this OpenClaw deployment when you want approvals from any OpenClaw channel (Telegram, Discord, gateway chat, Lessinbox, etc.) to resume through Lessinbox.

inboundCommandAuthorized defaults to false. Set it to true only when your Openclaw policy layer explicitly requires trusted channel-originated command execution.

Tool hook guardrails + approvals (OpenClaw before_tool_call / after_tool_call)

When channels.lessinbox.toolHooks.enabled is true, the plugin registers OpenClaw tool hooks that:

Canonical config path is channel-level (channels.lessinbox.toolHooks). Backward-compatible fallback is still supported at channels.lessinbox.accounts.<accountId>.toolHooks.

  • run Lessinbox guardrails evaluation for side-effecting tool calls (tool_call.side_effects)
  • pause the run when approval is required (via pauseRun) and optionally invoke waitForInput/resumeRun
  • emit a policy dry-run log record to the OpenClaw logger (JSON) with keys: sessionKey, runId, threadId, toolName, argsHash, decision, timestamp
  • persist dry-run + result artifacts to Lessinbox /v1/executions (best-effort) so timeline UI shows tool policy checks

If the OpenClaw runtime does not supply sessionKey, pauseRun, or next in the hook context, the plugin falls back to fail-closed evaluation in hook mode:

  • deny and require_approval outcomes are blocked.
  • allow outcomes are passed through to runtime continuation.

This fallback cannot provide deterministic pause/resume lifecycle guarantees by itself; reliable approval resume requires execution-layer support in OpenClaw core.

Set channels.lessinbox.toolHooks.persistExecutionArtifacts=false to keep dry-run logs only (no /v1/executions writes from hooks).

Set channels.lessinbox.toolHooks.debugPersistence=true to emit explicit success/failure debug lines for /v1/executions hook persistence attempts.

Openclaw wiring expectations

The plugin expects Openclaw to call outbound.sendText(...) with:

  • text (required)
  • config (runtime config object)
  • optional: accountId, target, channelId, title, threadId, runId, conversationId, metadata

If threadId and runId are missing, the plugin will create a new Lessinbox run and use that thread.

Target formats supported by the plugin:

  • channel:<channelId> - continue the latest thread for this channel (or create one if missing)
  • thread:<threadId> - create/continue conversation in that exact thread
  • raw channel id (cml... or chn_...) - treated as channel:<id>
  • account alias (default, etc.) - switches account when it matches configured account id

Realtime inbound usage

The package exports subscribeToLessinboxEvents(...) as a direct helper for custom integrations.

Example:

import { subscribeToLessinboxEvents } from "@lessinbox/openclaw-channel"

const unsubscribe = subscribeToLessinboxEvents({
  config: runtimeConfig,
  accountId: "default",
  onEvent: async (event) => {
    // event.kind, event.threadId, event.runId, event.conversationId, event.payload
  }
})

For Openclaw-native runtime lifecycle, inbound processing is wired through channel gateway hooks:

  • plugin.gateway.startAccount(...) subscribes to Lessinbox workspace stream.
  • plugin.gateway.stopAccount(...) unsubscribes and shuts down account listeners.
  • Incoming message.created user messages are routed through Openclaw runtime reply dispatcher.

This is the preferred runtime path in v1.

On terminal run states (completed, failed, canceled) the plugin keeps thread mapping and clears only the run id, so the next outbound message starts a fresh run in the same thread.

Guardrails middleware usage

Use this helper when Openclaw is about to execute a side-effecting tool action (email, payments, external writes).

import { executeLessinboxGuardedExecution } from "@lessinbox/openclaw-channel"

await executeLessinboxGuardedExecution({
  config: runtimeConfig,
  accountId: "default",
  scope: {
    work_item_id: "wi_123",
    run_id: "run_123",
    thread_id: "thr_123"
  },
  tool: "email",
  action: "send",
  request: {
    to: "[email protected]",
    subject: "Status update"
  },
  side_effects: {
    provider: "smtp"
  },
  onApprovalRequired: async ({ approval_request_id }) => {
    // Optional: wait until someone approves this request in Lessinbox.
    console.log("Approval required:", approval_request_id)
  },
  execute: async () => {
    // your real side effect call
    return { ok: true }
  }
})

This enforces Lessinbox policy decisions (deny, require_simulation, require_approval) and writes execution records before/after side effects.

Openclaw pre-tool middleware helper (approval pause/resume)

For Openclaw Option A middleware, use executeLessinboxOpenclawMiddleware(...).

  • On first pass, it returns status: "requires_approval" and does not execute the tool.
  • After human approval, call again with approval.approval_request_id to execute safely.
import { executeLessinboxOpenclawMiddleware } from "@lessinbox/openclaw-channel"

const decision = await executeLessinboxOpenclawMiddleware({
  config: runtimeConfig,
  accountId: "default",
  scope: {
    work_item_id: "wi_123",
    run_id: "run_123",
    thread_id: "thr_123"
  },
  tool_call: {
    tool_name: "payments.transfer",
    args: { method: "POST", amount: 5000 },
    side_effects: { provider: "banking" }
  },
  execute: async () => {
    // real side effect
    return { ok: true }
  }
})

if (decision.status === "requires_approval") {
  // pause Openclaw run and wait for interrupt.answered
}

if (decision.status === "executed") {
  console.log("Executed:", decision.result)
}

Openclaw tool-runner adapter helper (drop-in)

If you want one adapter function that also calls your Openclaw pauseRun(...) hook, use executeLessinboxOpenclawToolRunnerAdapter(...).

import { executeLessinboxOpenclawToolRunnerAdapter } from "@lessinbox/openclaw-channel"

const decision = await executeLessinboxOpenclawToolRunnerAdapter({
  config: runtimeConfig,
  accountId: "default",
  context: {
    session_key: "sess_123",
    work_item_id: "wi_123",
    run_id: "run_123",
    thread_id: "thr_123"
  },
  tool_call: {
    tool_name: "payments.transfer",
    args: { method: "POST", amount: 5000 },
    side_effects: { provider: "banking" }
  },
  pauseRun: async ({ approval_request_id }) => {
    // Persist approval id in Openclaw run/session state and pause execution.
    await pauseRun({ approval_request_id })
  },
  execute: async () => {
    // real side effect
    return { ok: true }
  }
})

if (decision.status === "blocked") {
  throw new Error(decision.reason)
}

if (decision.status === "paused_for_approval") {
  return
}

// decision.status === "executed"
console.log(decision.result)

Runtime provider sync (Skill Market)

This channel package does not hardcode Openclaw into Lessinbox. Register Openclaw as a runtime provider and sync its catalog:

  • POST /v1/runtime-providers
  • POST /v1/runtime-providers/:providerId/tools/sync
  • POST /v1/runtime-providers/:providerId/agents/sync
  • GET /v1/runtime-providers/agents?provider_id=:providerId

Use synced runtime_agent_id values in Agent Team definitions (/v1/agent-team/definitions).

Build

pnpm --filter @lessinbox/openclaw-channel build

Publish

pnpm --filter @lessinbox/openclaw-channel build
npm publish --access public