@alfe.ai/openclaw-chat 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as SessionSummary, S as SessionData, _ as createAlfeChannelPlugin, b as AlfeResolvedAccount, p as plugin, v as AlfeChannelConfig, x as ChatMessage, y as AlfePluginConfig } from "./plugin.cjs";
1
+ import { C as ChatMessage, S as AlfeResolvedAccount, T as SessionSummary, b as AlfeChannelConfig, h as plugin, w as SessionData, x as AlfePluginConfig, y as createAlfeChannelPlugin } from "./plugin.cjs";
2
2
  export { type AlfeChannelConfig, type AlfePluginConfig, type AlfeResolvedAccount, type ChatMessage, type SessionData, type SessionSummary, createAlfeChannelPlugin, plugin as default };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as SessionSummary, S as SessionData, _ as createAlfeChannelPlugin, b as AlfeResolvedAccount, p as plugin, v as AlfeChannelConfig, x as ChatMessage, y as AlfePluginConfig } from "./plugin.js";
1
+ import { C as ChatMessage, S as AlfeResolvedAccount, T as SessionSummary, b as AlfeChannelConfig, h as plugin, w as SessionData, x as AlfePluginConfig, y as createAlfeChannelPlugin } from "./plugin.js";
2
2
  export { type AlfeChannelConfig, type AlfePluginConfig, type AlfeResolvedAccount, type ChatMessage, type SessionData, type SessionSummary, createAlfeChannelPlugin, plugin as default };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { d as plugin, h as createAlfeChannelPlugin } from "./plugin2.js";
1
+ import { f as plugin, g as createAlfeChannelPlugin } from "./plugin2.js";
2
2
  export { createAlfeChannelPlugin, plugin as default };
package/dist/plugin.cjs CHANGED
@@ -12,6 +12,7 @@ exports.admitAgentEvent = require_plugin.admitAgentEvent;
12
12
  exports.buildA2ACompletePayload = require_plugin.buildA2ACompletePayload;
13
13
  exports.buildOriginEnvelopeContext = require_plugin.buildOriginEnvelopeContext;
14
14
  exports.buildToolActivity = require_plugin.buildToolActivity;
15
+ exports.computeOpenClawSdkAnchors = require_plugin.computeOpenClawSdkAnchors;
15
16
  exports.default = require_plugin.plugin;
16
17
  exports.joinAssistantText = require_plugin.joinAssistantText;
17
18
  exports.resolveAbortTargetKeys = require_plugin.resolveAbortTargetKeys;
package/dist/plugin.d.cts CHANGED
@@ -568,6 +568,38 @@ interface PluginApi {
568
568
  priority?: number;
569
569
  }): void;
570
570
  }
571
+ /**
572
+ * Injectable seams for anchor computation — real implementations resolve
573
+ * symlinks and locate the `openclaw` bin. Overridable so the pure anchor
574
+ * logic can be unit-tested without a homebrew/openclaw layout on disk.
575
+ */
576
+ interface AnchorEnv {
577
+ realpath: (p: string) => string;
578
+ which: (cmd: string) => string | undefined;
579
+ requireMainFilename: string | undefined;
580
+ argv1: string | undefined;
581
+ execPath: string;
582
+ }
583
+ /**
584
+ * Compute the ordered, de-duplicated list of resolver anchors to try, each
585
+ * best-effort `realpath`'d so a bin SYMLINK (e.g. `/opt/homebrew/bin/openclaw`
586
+ * → `../lib/node_modules/openclaw/openclaw.mjs`) resolves to the real module
587
+ * whose `createRequire` CAN find `openclaw/plugin-sdk/*`.
588
+ *
589
+ * Order (proven against homebrew-keg / relocated-node / bin-symlink layouts):
590
+ * 1. require.main?.filename, process.argv[1] — realpath'd (the bin symlink →
591
+ * real `openclaw.mjs`). argv[1] is often the symlink; realpath is what
592
+ * makes it a usable anchor.
593
+ * 2. `which openclaw` → realpath — the robust cross-setup anchor. On a
594
+ * homebrew keg, argv[1] is a symlink under `/opt/homebrew/bin` (no
595
+ * `node_modules` sibling); its realpath lands in the real
596
+ * `lib/node_modules/openclaw` tree where resolution succeeds.
597
+ * 3. execPath-derived global-modules path (the original Strategy 2), plus a
598
+ * realpath'd variant of the execPath prefix for relocated-node layouts.
599
+ *
600
+ * Pure except for the injected `realpath` / `which` seams — unit-tested.
601
+ */
602
+ declare function computeOpenClawSdkAnchors(deps: AnchorEnv): string[];
571
603
  declare function enqueueAgentTurn(task: () => Promise<void>, opts?: {
572
604
  coalesceKey?: string;
573
605
  onSuperseded?: () => void;
@@ -649,4 +681,4 @@ declare const plugin: {
649
681
  deactivate(api: PluginApi): Promise<void>;
650
682
  };
651
683
  //#endregion
652
- export { SessionSummary as C, SessionData as S, createAlfeChannelPlugin as _, __agentTurnQueueForTest as a, AlfeResolvedAccount as b, admitAgentEvent as c, buildToolActivity as d, joinAssistantText as f, stripLeakedToolSummary as g, rewriteAssistantText as h, VOICE_REPLY_SYSTEM_PROMPT as i, buildA2ACompletePayload as l, resolveAbortTargetKeys as m, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as n, __resetAgentTurnQueueForTest as o, plugin as p, RunEventGate as r, __setActiveRunForTest as s, ActiveRunRef as t, buildOriginEnvelopeContext as u, AlfeChannelConfig as v, ChatMessage as x, AlfePluginConfig as y };
684
+ export { ChatMessage as C, AlfeResolvedAccount as S, SessionSummary as T, rewriteAssistantText as _, VOICE_REPLY_SYSTEM_PROMPT as a, AlfeChannelConfig as b, __setActiveRunForTest as c, buildOriginEnvelopeContext as d, buildToolActivity as f, resolveAbortTargetKeys as g, plugin as h, RunEventGate as i, admitAgentEvent as l, joinAssistantText as m, AnchorEnv as n, __agentTurnQueueForTest as o, computeOpenClawSdkAnchors as p, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as r, __resetAgentTurnQueueForTest as s, ActiveRunRef as t, buildA2ACompletePayload as u, stripLeakedToolSummary as v, SessionData as w, AlfePluginConfig as x, createAlfeChannelPlugin as y };
package/dist/plugin.d.ts CHANGED
@@ -568,6 +568,38 @@ interface PluginApi {
568
568
  priority?: number;
569
569
  }): void;
570
570
  }
571
+ /**
572
+ * Injectable seams for anchor computation — real implementations resolve
573
+ * symlinks and locate the `openclaw` bin. Overridable so the pure anchor
574
+ * logic can be unit-tested without a homebrew/openclaw layout on disk.
575
+ */
576
+ interface AnchorEnv {
577
+ realpath: (p: string) => string;
578
+ which: (cmd: string) => string | undefined;
579
+ requireMainFilename: string | undefined;
580
+ argv1: string | undefined;
581
+ execPath: string;
582
+ }
583
+ /**
584
+ * Compute the ordered, de-duplicated list of resolver anchors to try, each
585
+ * best-effort `realpath`'d so a bin SYMLINK (e.g. `/opt/homebrew/bin/openclaw`
586
+ * → `../lib/node_modules/openclaw/openclaw.mjs`) resolves to the real module
587
+ * whose `createRequire` CAN find `openclaw/plugin-sdk/*`.
588
+ *
589
+ * Order (proven against homebrew-keg / relocated-node / bin-symlink layouts):
590
+ * 1. require.main?.filename, process.argv[1] — realpath'd (the bin symlink →
591
+ * real `openclaw.mjs`). argv[1] is often the symlink; realpath is what
592
+ * makes it a usable anchor.
593
+ * 2. `which openclaw` → realpath — the robust cross-setup anchor. On a
594
+ * homebrew keg, argv[1] is a symlink under `/opt/homebrew/bin` (no
595
+ * `node_modules` sibling); its realpath lands in the real
596
+ * `lib/node_modules/openclaw` tree where resolution succeeds.
597
+ * 3. execPath-derived global-modules path (the original Strategy 2), plus a
598
+ * realpath'd variant of the execPath prefix for relocated-node layouts.
599
+ *
600
+ * Pure except for the injected `realpath` / `which` seams — unit-tested.
601
+ */
602
+ declare function computeOpenClawSdkAnchors(deps: AnchorEnv): string[];
571
603
  declare function enqueueAgentTurn(task: () => Promise<void>, opts?: {
572
604
  coalesceKey?: string;
573
605
  onSuperseded?: () => void;
@@ -649,4 +681,4 @@ declare const plugin: {
649
681
  deactivate(api: PluginApi): Promise<void>;
650
682
  };
651
683
  //#endregion
652
- export { SessionSummary as C, SessionData as S, createAlfeChannelPlugin as _, __agentTurnQueueForTest as a, AlfeResolvedAccount as b, admitAgentEvent as c, buildToolActivity as d, joinAssistantText as f, stripLeakedToolSummary as g, rewriteAssistantText as h, VOICE_REPLY_SYSTEM_PROMPT as i, buildA2ACompletePayload as l, resolveAbortTargetKeys as m, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as n, __resetAgentTurnQueueForTest as o, plugin as p, RunEventGate as r, __setActiveRunForTest as s, ActiveRunRef as t, buildOriginEnvelopeContext as u, AlfeChannelConfig as v, ChatMessage as x, AlfePluginConfig as y };
684
+ export { ChatMessage as C, AlfeResolvedAccount as S, SessionSummary as T, rewriteAssistantText as _, VOICE_REPLY_SYSTEM_PROMPT as a, AlfeChannelConfig as b, __setActiveRunForTest as c, buildOriginEnvelopeContext as d, buildToolActivity as f, resolveAbortTargetKeys as g, plugin as h, RunEventGate as i, admitAgentEvent as l, joinAssistantText as m, AnchorEnv as n, __agentTurnQueueForTest as o, computeOpenClawSdkAnchors as p, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as r, __resetAgentTurnQueueForTest as s, ActiveRunRef as t, buildA2ACompletePayload as u, stripLeakedToolSummary as v, SessionData as w, AlfePluginConfig as x, createAlfeChannelPlugin as y };
package/dist/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as __setActiveRunForTest, c as buildOriginEnvelopeContext, d as plugin, f as resolveAbortTargetKeys, i as __resetAgentTurnQueueForTest, l as buildToolActivity, m as stripLeakedToolSummary, n as VOICE_REPLY_SYSTEM_PROMPT, o as admitAgentEvent, p as rewriteAssistantText, r as __agentTurnQueueForTest, s as buildA2ACompletePayload, t as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, u as joinAssistantText } from "./plugin2.js";
2
- export { CROSS_AGENT_TOOLS_SYSTEM_PROMPT, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildOriginEnvelopeContext, buildToolActivity, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
1
+ import { a as __setActiveRunForTest, c as buildOriginEnvelopeContext, d as joinAssistantText, f as plugin, h as stripLeakedToolSummary, i as __resetAgentTurnQueueForTest, l as buildToolActivity, m as rewriteAssistantText, n as VOICE_REPLY_SYSTEM_PROMPT, o as admitAgentEvent, p as resolveAbortTargetKeys, r as __agentTurnQueueForTest, s as buildA2ACompletePayload, t as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, u as computeOpenClawSdkAnchors } from "./plugin2.js";
2
+ export { CROSS_AGENT_TOOLS_SYSTEM_PROMPT, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildOriginEnvelopeContext, buildToolActivity, computeOpenClawSdkAnchors, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
package/dist/plugin2.cjs CHANGED
@@ -1,12 +1,13 @@
1
1
  let node_module = require("node:module");
2
2
  let node_fs_promises = require("node:fs/promises");
3
+ let node_fs = require("node:fs");
4
+ let node_child_process = require("node:child_process");
3
5
  let node_path = require("node:path");
4
6
  let node_os = require("node:os");
5
7
  let _alfe_ai_chat = require("@alfe.ai/chat");
6
8
  let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
7
9
  let node_crypto = require("node:crypto");
8
10
  let _alfe_ai_config = require("@alfe.ai/config");
9
- let node_fs = require("node:fs");
10
11
  //#region src/outbound-media.ts
11
12
  /**
12
13
  * Outbound media upload — agent → user.
@@ -2091,22 +2092,110 @@ function loadOpenClawSdk(req, log, anchor) {
2091
2092
  return resolvedDispatch;
2092
2093
  }
2093
2094
  /**
2095
+ * Default `which openclaw` implementation. Prefers the shell's own resolver
2096
+ * (`command -v` / `which`), then falls back to scanning `PATH` for an
2097
+ * `openclaw` executable. Returns undefined if it can't be located — every
2098
+ * caller treats a missing anchor as "skip", never as fatal.
2099
+ */
2100
+ function whichOpenClaw(pathEnv) {
2101
+ for (const probe of [{
2102
+ cmd: "command",
2103
+ args: ["-v", "openclaw"]
2104
+ }, {
2105
+ cmd: "which",
2106
+ args: ["openclaw"]
2107
+ }]) try {
2108
+ const out = (0, node_child_process.execFileSync)(probe.cmd, probe.args, {
2109
+ encoding: "utf8",
2110
+ stdio: [
2111
+ "ignore",
2112
+ "pipe",
2113
+ "ignore"
2114
+ ]
2115
+ }).trim().split("\n")[0]?.trim();
2116
+ if (out) return out;
2117
+ } catch {}
2118
+ for (const dir of (pathEnv ?? "").split(node_path.delimiter)) {
2119
+ if (!dir) continue;
2120
+ const candidate = (0, node_path.join)(dir, "openclaw");
2121
+ try {
2122
+ (0, node_fs.realpathSync)(candidate);
2123
+ return candidate;
2124
+ } catch {}
2125
+ }
2126
+ }
2127
+ /**
2128
+ * Compute the ordered, de-duplicated list of resolver anchors to try, each
2129
+ * best-effort `realpath`'d so a bin SYMLINK (e.g. `/opt/homebrew/bin/openclaw`
2130
+ * → `../lib/node_modules/openclaw/openclaw.mjs`) resolves to the real module
2131
+ * whose `createRequire` CAN find `openclaw/plugin-sdk/*`.
2132
+ *
2133
+ * Order (proven against homebrew-keg / relocated-node / bin-symlink layouts):
2134
+ * 1. require.main?.filename, process.argv[1] — realpath'd (the bin symlink →
2135
+ * real `openclaw.mjs`). argv[1] is often the symlink; realpath is what
2136
+ * makes it a usable anchor.
2137
+ * 2. `which openclaw` → realpath — the robust cross-setup anchor. On a
2138
+ * homebrew keg, argv[1] is a symlink under `/opt/homebrew/bin` (no
2139
+ * `node_modules` sibling); its realpath lands in the real
2140
+ * `lib/node_modules/openclaw` tree where resolution succeeds.
2141
+ * 3. execPath-derived global-modules path (the original Strategy 2), plus a
2142
+ * realpath'd variant of the execPath prefix for relocated-node layouts.
2143
+ *
2144
+ * Pure except for the injected `realpath` / `which` seams — unit-tested.
2145
+ */
2146
+ function computeOpenClawSdkAnchors(deps) {
2147
+ const anchors = [];
2148
+ const push = (p) => {
2149
+ if (!p) return;
2150
+ let real;
2151
+ try {
2152
+ real = deps.realpath(p);
2153
+ } catch {}
2154
+ for (const candidate of [real, p]) if (candidate && !anchors.includes(candidate)) anchors.push(candidate);
2155
+ };
2156
+ push(deps.requireMainFilename);
2157
+ push(deps.argv1);
2158
+ try {
2159
+ push(deps.which("openclaw"));
2160
+ } catch {}
2161
+ for (const base of [deps.execPath, safeRealpath(deps.realpath, deps.execPath)]) {
2162
+ if (!base) continue;
2163
+ try {
2164
+ const derived = (0, node_path.join)((0, node_path.resolve)((0, node_path.dirname)(base), ".."), "lib", "node_modules", "openclaw", "package.json");
2165
+ if (!anchors.includes(derived)) anchors.push(derived);
2166
+ } catch {}
2167
+ }
2168
+ return anchors;
2169
+ }
2170
+ function safeRealpath(realpath, p) {
2171
+ try {
2172
+ const r = realpath(p);
2173
+ return r === p ? void 0 : r;
2174
+ } catch {
2175
+ return;
2176
+ }
2177
+ }
2178
+ /**
2094
2179
  * Resolve OpenClaw SDK functions from the running process.
2095
2180
  *
2096
2181
  * The openclaw package is NOT in the plugin's node_modules (it's a peer dep).
2097
2182
  * Since the plugin runs inside OpenClaw's process, we anchor resolution to
2098
- * OpenClaw's own entry module via require.main, then fall back to deriving
2099
- * the global modules path from process.execPath.
2183
+ * OpenClaw's own realpath'd entry (surviving bin-symlink / homebrew-keg /
2184
+ * relocated-node layouts), then to a `which openclaw` lookup, then fall back to
2185
+ * deriving the global modules path from process.execPath.
2100
2186
  */
2101
2187
  function resolveOpenClawSdk(log) {
2102
- const anchors = [require$1.main?.filename, process.argv[1]].filter(Boolean);
2188
+ const pathEnv = process.env.PATH;
2189
+ const anchors = computeOpenClawSdkAnchors({
2190
+ realpath: node_fs.realpathSync,
2191
+ which: () => whichOpenClaw(pathEnv),
2192
+ requireMainFilename: require$1.main?.filename,
2193
+ argv1: process.argv[1],
2194
+ execPath: process.execPath
2195
+ });
2103
2196
  for (const anchor of anchors) try {
2104
2197
  if (loadOpenClawSdk((0, node_module.createRequire)(anchor), log, anchor)) return;
2105
2198
  } catch {}
2106
- try {
2107
- const derivedPath = (0, node_path.join)((0, node_path.resolve)((0, node_path.dirname)(process.execPath), ".."), "lib", "node_modules", "openclaw", "package.json");
2108
- if (loadOpenClawSdk((0, node_module.createRequire)(derivedPath), log, derivedPath)) return;
2109
- } catch {}
2110
2199
  log.warn("OpenClaw SDK not resolvable — chat dispatch will not work");
2111
2200
  }
2112
2201
  let pluginRuntime = null;
@@ -2868,8 +2957,9 @@ const plugin = {
2868
2957
  activity: await resolveHistoryActivity(session)
2869
2958
  };
2870
2959
  });
2960
+ api.registerGatewayMethod("runtime.activity", () => Promise.resolve({ active: activeRun !== null }));
2871
2961
  gw.__alfeChatGatewayMethodsRegistered = true;
2872
- log.info("Registered gateway RPC methods: sessions.list, sessions.get");
2962
+ log.info("Registered gateway RPC methods: sessions.list, sessions.get, runtime.activity");
2873
2963
  }
2874
2964
  api.on("session_start", async (...eventArgs) => {
2875
2965
  if (!pluginRuntime) return;
@@ -2977,6 +3067,12 @@ Object.defineProperty(exports, "buildToolActivity", {
2977
3067
  return buildToolActivity;
2978
3068
  }
2979
3069
  });
3070
+ Object.defineProperty(exports, "computeOpenClawSdkAnchors", {
3071
+ enumerable: true,
3072
+ get: function() {
3073
+ return computeOpenClawSdkAnchors;
3074
+ }
3075
+ });
2980
3076
  Object.defineProperty(exports, "createAlfeChannelPlugin", {
2981
3077
  enumerable: true,
2982
3078
  get: function() {
@@ -1,2 +1,2 @@
1
- import { a as __agentTurnQueueForTest, c as admitAgentEvent, d as buildToolActivity, f as joinAssistantText, g as stripLeakedToolSummary, h as rewriteAssistantText, i as VOICE_REPLY_SYSTEM_PROMPT, l as buildA2ACompletePayload, m as resolveAbortTargetKeys, n as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, o as __resetAgentTurnQueueForTest, p as plugin, r as RunEventGate, s as __setActiveRunForTest, t as ActiveRunRef, u as buildOriginEnvelopeContext } from "./plugin.cjs";
2
- export { ActiveRunRef, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildOriginEnvelopeContext, buildToolActivity, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
1
+ import { _ as rewriteAssistantText, a as VOICE_REPLY_SYSTEM_PROMPT, c as __setActiveRunForTest, d as buildOriginEnvelopeContext, f as buildToolActivity, g as resolveAbortTargetKeys, h as plugin, i as RunEventGate, l as admitAgentEvent, m as joinAssistantText, n as AnchorEnv, o as __agentTurnQueueForTest, p as computeOpenClawSdkAnchors, r as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, s as __resetAgentTurnQueueForTest, t as ActiveRunRef, u as buildA2ACompletePayload, v as stripLeakedToolSummary } from "./plugin.cjs";
2
+ export { ActiveRunRef, AnchorEnv, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildOriginEnvelopeContext, buildToolActivity, computeOpenClawSdkAnchors, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
package/dist/plugin2.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as __agentTurnQueueForTest, c as admitAgentEvent, d as buildToolActivity, f as joinAssistantText, g as stripLeakedToolSummary, h as rewriteAssistantText, i as VOICE_REPLY_SYSTEM_PROMPT, l as buildA2ACompletePayload, m as resolveAbortTargetKeys, n as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, o as __resetAgentTurnQueueForTest, p as plugin, r as RunEventGate, s as __setActiveRunForTest, t as ActiveRunRef, u as buildOriginEnvelopeContext } from "./plugin.js";
2
- export { ActiveRunRef, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildOriginEnvelopeContext, buildToolActivity, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
1
+ import { _ as rewriteAssistantText, a as VOICE_REPLY_SYSTEM_PROMPT, c as __setActiveRunForTest, d as buildOriginEnvelopeContext, f as buildToolActivity, g as resolveAbortTargetKeys, h as plugin, i as RunEventGate, l as admitAgentEvent, m as joinAssistantText, n as AnchorEnv, o as __agentTurnQueueForTest, p as computeOpenClawSdkAnchors, r as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, s as __resetAgentTurnQueueForTest, t as ActiveRunRef, u as buildA2ACompletePayload, v as stripLeakedToolSummary } from "./plugin.js";
2
+ export { ActiveRunRef, AnchorEnv, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildOriginEnvelopeContext, buildToolActivity, computeOpenClawSdkAnchors, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
package/dist/plugin2.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { mkdir, open, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
3
- import { basename, dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { existsSync, realpathSync } from "node:fs";
4
+ import { execFileSync } from "node:child_process";
5
+ import { basename, delimiter, dirname, isAbsolute, join, resolve } from "node:path";
4
6
  import { homedir } from "node:os";
5
7
  import { ChatServiceClient, resolveAlfeChat } from "@alfe.ai/chat";
6
8
  import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
7
9
  import { randomUUID } from "node:crypto";
8
10
  import { resolveConfig } from "@alfe.ai/config";
9
- import { existsSync } from "node:fs";
10
11
  //#region src/outbound-media.ts
11
12
  /**
12
13
  * Outbound media upload — agent → user.
@@ -2091,22 +2092,110 @@ function loadOpenClawSdk(req, log, anchor) {
2091
2092
  return resolvedDispatch;
2092
2093
  }
2093
2094
  /**
2095
+ * Default `which openclaw` implementation. Prefers the shell's own resolver
2096
+ * (`command -v` / `which`), then falls back to scanning `PATH` for an
2097
+ * `openclaw` executable. Returns undefined if it can't be located — every
2098
+ * caller treats a missing anchor as "skip", never as fatal.
2099
+ */
2100
+ function whichOpenClaw(pathEnv) {
2101
+ for (const probe of [{
2102
+ cmd: "command",
2103
+ args: ["-v", "openclaw"]
2104
+ }, {
2105
+ cmd: "which",
2106
+ args: ["openclaw"]
2107
+ }]) try {
2108
+ const out = execFileSync(probe.cmd, probe.args, {
2109
+ encoding: "utf8",
2110
+ stdio: [
2111
+ "ignore",
2112
+ "pipe",
2113
+ "ignore"
2114
+ ]
2115
+ }).trim().split("\n")[0]?.trim();
2116
+ if (out) return out;
2117
+ } catch {}
2118
+ for (const dir of (pathEnv ?? "").split(delimiter)) {
2119
+ if (!dir) continue;
2120
+ const candidate = join(dir, "openclaw");
2121
+ try {
2122
+ realpathSync(candidate);
2123
+ return candidate;
2124
+ } catch {}
2125
+ }
2126
+ }
2127
+ /**
2128
+ * Compute the ordered, de-duplicated list of resolver anchors to try, each
2129
+ * best-effort `realpath`'d so a bin SYMLINK (e.g. `/opt/homebrew/bin/openclaw`
2130
+ * → `../lib/node_modules/openclaw/openclaw.mjs`) resolves to the real module
2131
+ * whose `createRequire` CAN find `openclaw/plugin-sdk/*`.
2132
+ *
2133
+ * Order (proven against homebrew-keg / relocated-node / bin-symlink layouts):
2134
+ * 1. require.main?.filename, process.argv[1] — realpath'd (the bin symlink →
2135
+ * real `openclaw.mjs`). argv[1] is often the symlink; realpath is what
2136
+ * makes it a usable anchor.
2137
+ * 2. `which openclaw` → realpath — the robust cross-setup anchor. On a
2138
+ * homebrew keg, argv[1] is a symlink under `/opt/homebrew/bin` (no
2139
+ * `node_modules` sibling); its realpath lands in the real
2140
+ * `lib/node_modules/openclaw` tree where resolution succeeds.
2141
+ * 3. execPath-derived global-modules path (the original Strategy 2), plus a
2142
+ * realpath'd variant of the execPath prefix for relocated-node layouts.
2143
+ *
2144
+ * Pure except for the injected `realpath` / `which` seams — unit-tested.
2145
+ */
2146
+ function computeOpenClawSdkAnchors(deps) {
2147
+ const anchors = [];
2148
+ const push = (p) => {
2149
+ if (!p) return;
2150
+ let real;
2151
+ try {
2152
+ real = deps.realpath(p);
2153
+ } catch {}
2154
+ for (const candidate of [real, p]) if (candidate && !anchors.includes(candidate)) anchors.push(candidate);
2155
+ };
2156
+ push(deps.requireMainFilename);
2157
+ push(deps.argv1);
2158
+ try {
2159
+ push(deps.which("openclaw"));
2160
+ } catch {}
2161
+ for (const base of [deps.execPath, safeRealpath(deps.realpath, deps.execPath)]) {
2162
+ if (!base) continue;
2163
+ try {
2164
+ const derived = join(resolve(dirname(base), ".."), "lib", "node_modules", "openclaw", "package.json");
2165
+ if (!anchors.includes(derived)) anchors.push(derived);
2166
+ } catch {}
2167
+ }
2168
+ return anchors;
2169
+ }
2170
+ function safeRealpath(realpath, p) {
2171
+ try {
2172
+ const r = realpath(p);
2173
+ return r === p ? void 0 : r;
2174
+ } catch {
2175
+ return;
2176
+ }
2177
+ }
2178
+ /**
2094
2179
  * Resolve OpenClaw SDK functions from the running process.
2095
2180
  *
2096
2181
  * The openclaw package is NOT in the plugin's node_modules (it's a peer dep).
2097
2182
  * Since the plugin runs inside OpenClaw's process, we anchor resolution to
2098
- * OpenClaw's own entry module via require.main, then fall back to deriving
2099
- * the global modules path from process.execPath.
2183
+ * OpenClaw's own realpath'd entry (surviving bin-symlink / homebrew-keg /
2184
+ * relocated-node layouts), then to a `which openclaw` lookup, then fall back to
2185
+ * deriving the global modules path from process.execPath.
2100
2186
  */
2101
2187
  function resolveOpenClawSdk(log) {
2102
- const anchors = [require.main?.filename, process.argv[1]].filter(Boolean);
2188
+ const pathEnv = process.env.PATH;
2189
+ const anchors = computeOpenClawSdkAnchors({
2190
+ realpath: realpathSync,
2191
+ which: () => whichOpenClaw(pathEnv),
2192
+ requireMainFilename: require.main?.filename,
2193
+ argv1: process.argv[1],
2194
+ execPath: process.execPath
2195
+ });
2103
2196
  for (const anchor of anchors) try {
2104
2197
  if (loadOpenClawSdk(createRequire(anchor), log, anchor)) return;
2105
2198
  } catch {}
2106
- try {
2107
- const derivedPath = join(resolve(dirname(process.execPath), ".."), "lib", "node_modules", "openclaw", "package.json");
2108
- if (loadOpenClawSdk(createRequire(derivedPath), log, derivedPath)) return;
2109
- } catch {}
2110
2199
  log.warn("OpenClaw SDK not resolvable — chat dispatch will not work");
2111
2200
  }
2112
2201
  let pluginRuntime = null;
@@ -2868,8 +2957,9 @@ const plugin = {
2868
2957
  activity: await resolveHistoryActivity(session)
2869
2958
  };
2870
2959
  });
2960
+ api.registerGatewayMethod("runtime.activity", () => Promise.resolve({ active: activeRun !== null }));
2871
2961
  gw.__alfeChatGatewayMethodsRegistered = true;
2872
- log.info("Registered gateway RPC methods: sessions.list, sessions.get");
2962
+ log.info("Registered gateway RPC methods: sessions.list, sessions.get, runtime.activity");
2873
2963
  }
2874
2964
  api.on("session_start", async (...eventArgs) => {
2875
2965
  if (!pluginRuntime) return;
@@ -2923,4 +3013,4 @@ const plugin = {
2923
3013
  }
2924
3014
  };
2925
3015
  //#endregion
2926
- export { __setActiveRunForTest as a, buildOriginEnvelopeContext as c, plugin as d, resolveAbortTargetKeys as f, createAlfeChannelPlugin as h, __resetAgentTurnQueueForTest as i, buildToolActivity as l, stripLeakedToolSummary as m, VOICE_REPLY_SYSTEM_PROMPT as n, admitAgentEvent as o, rewriteAssistantText as p, __agentTurnQueueForTest as r, buildA2ACompletePayload as s, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as t, joinAssistantText as u };
3016
+ export { __setActiveRunForTest as a, buildOriginEnvelopeContext as c, joinAssistantText as d, plugin as f, createAlfeChannelPlugin as g, stripLeakedToolSummary as h, __resetAgentTurnQueueForTest as i, buildToolActivity as l, rewriteAssistantText as m, VOICE_REPLY_SYSTEM_PROMPT as n, admitAgentEvent as o, resolveAbortTargetKeys as p, __agentTurnQueueForTest as r, buildA2ACompletePayload as s, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as t, computeOpenClawSdkAnchors as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-chat",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "OpenClaw chat plugin for Alfe — web widget and mobile app channels",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin.js",
@@ -27,7 +27,7 @@
27
27
  "openclaw.plugin.json"
28
28
  ],
29
29
  "dependencies": {
30
- "@alfe.ai/agent-api-client": "^0.9.0",
30
+ "@alfe.ai/agent-api-client": "^0.10.0",
31
31
  "@alfe.ai/chat": "^0.0.16",
32
32
  "@alfe.ai/config": "^0.3.0"
33
33
  },