// src/openclaw-entry.ts
import { resolve } from "node:path";
var _ocRuntime = null;
var _channels = /* @__PURE__ */ new Map();
var _messageQueue = [];
function _setRuntime(rt) {
  _ocRuntime = rt;
  if (_messageQueue.length > 0) {
    const pending = _messageQueue.splice(0);
    for (const fn of pending) {
      fn().catch(() => {
      });
    }
  }
}
function listAccountIds(cfg) {
  return cfg?.channels?.agentvault?.dataDir ? ["default"] : [];
}
function resolveAccount(cfg, accountId) {
  const av = cfg?.channels?.agentvault ?? {};
  return {
    accountId: accountId ?? "default",
    dataDir: av.dataDir ?? "~/.openclaw/agentvault",
    apiUrl: av.apiUrl ?? "https://api.agentvault.chat",
    agentName: av.agentName ?? "OpenClaw Agent",
    httpPort: av.httpPort ?? 18790,
    configured: Boolean(av.dataDir)
  };
}
async function handleInbound(params) {
  const { plaintext, metadata, channel, account, cfg } = params;
  try {
    channel.sendTyping();
  } catch {
  }
  const core = _ocRuntime;
  const isRoomMessage = Boolean(metadata.roomId);
  const route = core.channel.routing.resolveAgentRoute({
    cfg,
    channel: "agentvault",
    accountId: account.accountId,
    peer: { kind: "direct", id: isRoomMessage ? `agentvault:room:${metadata.roomId}` : "agentvault:owner" }
  });
  const storePath = core.channel.session.resolveStorePath(cfg?.session?.store, {
    agentId: route.agentId
  });
  const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
  const previousTimestamp = core.channel.session.readSessionUpdatedAt({
    storePath,
    sessionKey: route.sessionKey
  });
  const body = core.channel.reply.formatAgentEnvelope({
    channel: "AgentVault",
    from: isRoomMessage ? "Room" : "Owner",
    timestamp: new Date(metadata.timestamp).getTime(),
    previousTimestamp,
    envelope: envelopeOptions,
    body: plaintext
  });
  const attachmentFields = {};
  if (metadata.attachment) {
    attachmentFields.AttachmentPath = metadata.attachment.filePath;
    attachmentFields.AttachmentFilename = metadata.attachment.filename;
    attachmentFields.AttachmentMime = metadata.attachment.mime;
    if (metadata.attachment.base64) {
      attachmentFields.MediaUrl = metadata.attachment.base64;
      attachmentFields.NumMedia = "1";
    }
  }
  const ctxPayload = core.channel.reply.finalizeInboundContext({
    Body: body,
    RawBody: plaintext,
    CommandBody: plaintext,
    From: isRoomMessage ? `agentvault:room:${metadata.roomId}` : "agentvault:owner",
    To: `agentvault:agent:${account.accountId}`,
    SessionKey: route.sessionKey,
    AccountId: account.accountId,
    ChatType: isRoomMessage ? "room" : "direct",
    ConversationLabel: isRoomMessage ? "AgentVault Room" : "AgentVault",
    SenderName: isRoomMessage ? "Room" : "Owner",
    SenderId: isRoomMessage ? `agentvault:room:${metadata.roomId}` : "agentvault:owner",
    Provider: "agentvault",
    Surface: "agentvault",
    MessageSid: metadata.messageId,
    Timestamp: new Date(metadata.timestamp).getTime(),
    OriginatingChannel: "agentvault",
    OriginatingTo: `agentvault:agent:${account.accountId}`,
    CommandAuthorized: true,
    ...attachmentFields
  });
  await core.channel.session.recordInboundSession({
    storePath,
    sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
    ctx: ctxPayload,
    onRecordError: (err) => {
      core.error?.(`[AgentVault] session record failed: ${String(err)}`);
    }
  });
  await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
    ctx: ctxPayload,
    cfg,
    dispatcherOptions: {
      deliver: async (payload) => {
        if (payload.kind === "thinking" || payload.kind === "reasoning") return;
        const text = (payload.text ?? "").trim();
        if (!text) return;
        if (/^(Reasoning|Thinking|Let me think|I need to|Let me check):/i.test(text)) return;
        if (isRoomMessage) {
          await channel.sendToRoom(metadata.roomId, text);
        } else {
          await channel.send(text, { topicId: metadata.topicId });
        }
      },
      onError: (err, info) => {
        core.error?.(`[AgentVault] ${info?.kind ?? "reply"} error: ${String(err)}`);
      }
    },
    replyOptions: {}
  });
}
var agentVaultPlugin = {
  id: "agentvault",
  meta: {
    id: "agentvault",
    label: "AgentVault",
    selectionLabel: "AgentVault (E2E Encrypted)",
    docsPath: "https://agentvault.chat/docs",
    blurb: "Zero-knowledge, end-to-end encrypted messaging between owners and their AI agents.",
    aliases: ["av", "agent-vault"]
  },
  capabilities: { chatTypes: ["direct"] },
  config: { listAccountIds, resolveAccount },
  gateway: {
    startAccount: async (ctx) => {
      const { account, cfg, log, abortSignal } = ctx;
      if (!account.configured) {
        throw new Error(
          "AgentVault channel not configured. Run: npx @agentvault/agentvault setup --token=av_tok_...\nThen restart OpenClaw."
        );
      }
      const dataDir = resolve(account.dataDir.replace(/^~/, process.env.HOME ?? "~"));
      log?.info(`[AgentVault] starting (dataDir=${dataDir})`);
      const { SecureChannel } = await import("./index.js");
      await new Promise((resolve2, reject) => {
        let channel;
        const onAbort = async () => {
          await channel?.stop();
          _channels.delete(account.accountId);
          resolve2();
        };
        abortSignal?.addEventListener("abort", () => void onAbort());
        import("./index.js").then(({ SecureChannel: SecureChannel2 }) => {
          channel = new SecureChannel2({
            inviteToken: "",
            dataDir,
            apiUrl: account.apiUrl,
            agentName: account.agentName,
            onMessage: async (plaintext, metadata) => {
              if (!_ocRuntime) {
                log?.info("[AgentVault] runtime not ready, queuing message");
                _messageQueue.push(async () => {
                  await handleInbound({ plaintext, metadata, channel, account, cfg });
                });
                return;
              }
              try {
                await handleInbound({ plaintext, metadata, channel, account, cfg });
              } catch (err) {
                log?.info(`[AgentVault] inbound error: ${String(err)}`);
              }
            },
            onStateChange: (state) => {
              log?.info(`[AgentVault] \u2192 ${state}`);
              if (state === "error") reject(new Error("AgentVault channel permanent error"));
            }
          });
          _channels.set(account.accountId, channel);
          channel.on("error", (err) => {
            log?.info(`[AgentVault] channel error (non-fatal): ${String(err)}`);
          });
          const httpPort = account.httpPort ?? 18790;
          channel.on("ready", () => {
            channel.startHttpServer(httpPort);
            log?.info(`[AgentVault] HTTP send server listening on http://127.0.0.1:${httpPort}`);
          });
          channel.start().catch(reject);
        }).catch(reject);
      });
      return { stop: async () => {
      } };
    }
  },
  outbound: {
    deliveryMode: "direct",
    // Register valid send targets so OpenClaw's `message` tool can route
    // proactive (agent-initiated) sends — not just replies to inbound messages.
    targets: [
      {
        id: "owner",
        label: "AgentVault Owner",
        accountId: "default"
      },
      {
        id: "default",
        label: "AgentVault Owner (default)",
        accountId: "default"
      }
    ],
    sendText: async ({ to, text, accountId }) => {
      const resolvedId = accountId ?? "default";
      const ch = _channels.get(resolvedId);
      if (!ch) return { ok: false, error: "AgentVault channel not connected" };
      try {
        const wasReady = ch.state === "ready";
        await ch.send(text);
        return { ok: true, queued: !wasReady };
      } catch (err) {
        return { ok: false, error: String(err) };
      }
    },
    sendMedia: async ({ to, text, mediaUrl, accountId }) => {
      const resolvedId = accountId ?? "default";
      const ch = _channels.get(resolvedId);
      if (!ch) return { ok: false, error: "AgentVault channel not connected" };
      try {
        const message = text ? `${text}
${mediaUrl}` : mediaUrl;
        const wasReady = ch.state === "ready";
        await ch.send(message);
        return { ok: true, queued: !wasReady };
      } catch (err) {
        return { ok: false, error: String(err) };
      }
    }
  }
};
var openclaw_entry_default = {
  id: "agentvault",
  name: "AgentVault",
  description: "End-to-end encrypted, zero-knowledge messaging between AI agent owners and their agents.",
  register(api) {
    _setRuntime(api.runtime);
    api.registerChannel({ plugin: agentVaultPlugin });
  }
};
export {
  openclaw_entry_default as default
};
//# sourceMappingURL=openclaw-entry.js.map
