@ken-jo/agent-connector 0.4.0 → 0.4.1

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.
Files changed (182) hide show
  1. package/README.md +8 -8
  2. package/dist/{action-PLZW5BGX.js → action-WT7GVRCC.js} +11 -10
  3. package/dist/{action-PLZW5BGX.js.map → action-WT7GVRCC.js.map} +1 -1
  4. package/dist/amazon-q-COAL3764.js +276 -0
  5. package/dist/amazon-q-COAL3764.js.map +1 -0
  6. package/dist/{amp-B524U6LO.js → amp-4U33CU7E.js} +3 -3
  7. package/dist/{antigravity-ANDKF3K7.js → antigravity-AMKKKHS4.js} +4 -4
  8. package/dist/{antigravity-cli-LLRPDDOT.js → antigravity-cli-XERXEH5W.js} +4 -4
  9. package/dist/{chunk-KGJWE6A2.js → chunk-24IDL5RW.js} +2 -2
  10. package/dist/{chunk-CIRDOY2V.js → chunk-2AVLX7GH.js} +3 -3
  11. package/dist/{chunk-ADTHT2FN.js → chunk-4ENJK5S2.js} +10 -173
  12. package/dist/chunk-4ENJK5S2.js.map +1 -0
  13. package/dist/{chunk-BHUCB6LY.js → chunk-5OWS5CMA.js} +8 -7
  14. package/dist/{chunk-BHUCB6LY.js.map → chunk-5OWS5CMA.js.map} +1 -1
  15. package/dist/{chunk-IW77FFNX.js → chunk-CPLK7GTE.js} +41 -5
  16. package/dist/chunk-CPLK7GTE.js.map +1 -0
  17. package/dist/chunk-DCKFYWGM.js +237 -0
  18. package/dist/chunk-DCKFYWGM.js.map +1 -0
  19. package/dist/{chunk-J3D5W5T2.js → chunk-EGCBZ2DC.js} +2 -2
  20. package/dist/{chunk-22Y24V7O.js → chunk-F5RX62T7.js} +2 -2
  21. package/dist/chunk-FAHIMJRU.js +177 -0
  22. package/dist/chunk-FAHIMJRU.js.map +1 -0
  23. package/dist/{chunk-BXGFUENQ.js → chunk-I5MLE6S7.js} +14 -1
  24. package/dist/{chunk-BXGFUENQ.js.map → chunk-I5MLE6S7.js.map} +1 -1
  25. package/dist/{chunk-LL4OBGRU.js → chunk-LQRAPXS4.js} +3 -3
  26. package/dist/{chunk-KGXDYGNJ.js → chunk-O5TWCIJN.js} +19 -19
  27. package/dist/{chunk-KGXDYGNJ.js.map → chunk-O5TWCIJN.js.map} +1 -1
  28. package/dist/{chunk-MAH6QQTO.js → chunk-QREL7LZJ.js} +5 -4
  29. package/dist/chunk-QREL7LZJ.js.map +1 -0
  30. package/dist/{chunk-G3ER4SY5.js → chunk-RIMVWCZY.js} +9 -9
  31. package/dist/{chunk-6XHB6BE3.js → chunk-SCVBYSF7.js} +1 -1
  32. package/dist/{chunk-KGGBNKI4.js → chunk-URHQMFB3.js} +90 -13
  33. package/dist/chunk-URHQMFB3.js.map +1 -0
  34. package/dist/chunk-WZA4CSGJ.js +28 -0
  35. package/dist/chunk-WZA4CSGJ.js.map +1 -0
  36. package/dist/{chunk-76FUSVIA.js → chunk-XJ4S47CT.js} +6 -5
  37. package/dist/chunk-XJ4S47CT.js.map +1 -0
  38. package/dist/{claude-code-MLWS7CW6.js → claude-code-W4PUFZL5.js} +20 -30
  39. package/dist/claude-code-W4PUFZL5.js.map +1 -0
  40. package/dist/cli/sdk.js +5 -4
  41. package/dist/cli/sdk.js.map +1 -1
  42. package/dist/cli.js +1 -1
  43. package/dist/cline-WRYMNS7Z.js +442 -0
  44. package/dist/cline-WRYMNS7Z.js.map +1 -0
  45. package/dist/{codebuff-SH2S3QAN.js → codebuff-DH5Y7GAT.js} +8 -4
  46. package/dist/codebuff-DH5Y7GAT.js.map +1 -0
  47. package/dist/{codex-BXA4FIF4.js → codex-DDHIV767.js} +43 -11
  48. package/dist/codex-DDHIV767.js.map +1 -0
  49. package/dist/continue-K43SUHLQ.js +790 -0
  50. package/dist/continue-K43SUHLQ.js.map +1 -0
  51. package/dist/{copilot-cli-ROIBTTDT.js → copilot-cli-Q3SSPT4V.js} +70 -12
  52. package/dist/copilot-cli-Q3SSPT4V.js.map +1 -0
  53. package/dist/{crush-QLEFLHUC.js → crush-MBEH2R3I.js} +130 -6
  54. package/dist/crush-MBEH2R3I.js.map +1 -0
  55. package/dist/{cursor-ASJPWZ4R.js → cursor-TMBXDYR4.js} +119 -19
  56. package/dist/cursor-TMBXDYR4.js.map +1 -0
  57. package/dist/{detect-ZIIS55LD.js → detect-KG5FG3EV.js} +5 -4
  58. package/dist/{detect-ZIIS55LD.js.map → detect-KG5FG3EV.js.map} +1 -1
  59. package/dist/{doctor-TJUGSQ5C.js → doctor-FLZF34Y3.js} +221 -95
  60. package/dist/doctor-FLZF34Y3.js.map +1 -0
  61. package/dist/{droid-NCSS3B5Y.js → droid-JC2NT6S4.js} +135 -13
  62. package/dist/droid-JC2NT6S4.js.map +1 -0
  63. package/dist/{gemini-cli-SLVWLXJW.js → gemini-cli-ILR7M5X3.js} +62 -10
  64. package/dist/gemini-cli-ILR7M5X3.js.map +1 -0
  65. package/dist/{goose-PBA7KTE5.js → goose-SFO5BBGW.js} +15 -8
  66. package/dist/goose-SFO5BBGW.js.map +1 -0
  67. package/dist/{hermes-ONDCECP2.js → hermes-3N7WN4WS.js} +329 -15
  68. package/dist/hermes-3N7WN4WS.js.map +1 -0
  69. package/dist/{hook-OU522ABV.js → hook-XR3Z45FB.js} +14 -11
  70. package/dist/hook-XR3Z45FB.js.map +1 -0
  71. package/dist/index.d.ts +2 -2
  72. package/dist/index.js +3 -2
  73. package/dist/{install-UDCBL7HP.js → install-BSR62UHJ.js} +11 -10
  74. package/dist/{install-UDCBL7HP.js.map → install-BSR62UHJ.js.map} +1 -1
  75. package/dist/{introspect-DuZSPSHB.d.ts → introspect-Nw8hTReX.d.ts} +1 -1
  76. package/dist/{jetbrains-copilot-YABZ3HFA.js → jetbrains-copilot-NRZHI5PC.js} +85 -18
  77. package/dist/jetbrains-copilot-NRZHI5PC.js.map +1 -0
  78. package/dist/{kilo-F3ZDZKCT.js → kilo-5NWOXVSD.js} +16 -16
  79. package/dist/kilo-5NWOXVSD.js.map +1 -0
  80. package/dist/{kilo-cli-3HWPWBY4.js → kilo-cli-UZ7R5UOP.js} +3 -3
  81. package/dist/{kimi-NV72FVAO.js → kimi-2YFK2GBA.js} +87 -25
  82. package/dist/kimi-2YFK2GBA.js.map +1 -0
  83. package/dist/{kiro-C5W6AGDR.js → kiro-YL6AWUYG.js} +3 -3
  84. package/dist/{leaderboard-TIBVONVT.js → leaderboard-MJG24O35.js} +3 -3
  85. package/dist/{mimo-code-IMM5YM3M.js → mimo-code-FTMGSKMQ.js} +41 -5
  86. package/dist/mimo-code-FTMGSKMQ.js.map +1 -0
  87. package/dist/{mux-DSSH4AFS.js → mux-5XZFB44C.js} +156 -6
  88. package/dist/mux-5XZFB44C.js.map +1 -0
  89. package/dist/{nemoclaw-3EVNKD3I.js → nemoclaw-W5KEJ3WX.js} +4 -4
  90. package/dist/{omp-Z6FRNDPN.js → omp-2QMEY6HD.js} +138 -18
  91. package/dist/omp-2QMEY6HD.js.map +1 -0
  92. package/dist/{openclaw-7G437NT7.js → openclaw-EXJEFNZI.js} +4 -4
  93. package/dist/{opencode-Q6KBSAKW.js → opencode-WNXYJR4X.js} +84 -27
  94. package/dist/opencode-WNXYJR4X.js.map +1 -0
  95. package/dist/{package-RMPN57YZ.js → package-ACZXJHWB.js} +8 -7
  96. package/dist/{package-RMPN57YZ.js.map → package-ACZXJHWB.js.map} +1 -1
  97. package/dist/{pi-LZT6WWAZ.js → pi-PVVPVWKG.js} +3 -2
  98. package/dist/{pi-LZT6WWAZ.js.map → pi-PVVPVWKG.js.map} +1 -1
  99. package/dist/{qwen-code-WQBKRPWJ.js → qwen-code-G4CXKUHR.js} +426 -10
  100. package/dist/qwen-code-G4CXKUHR.js.map +1 -0
  101. package/dist/{roo-code-GWSNU3OW.js → roo-code-67TYJIQR.js} +3 -3
  102. package/dist/runtime/index.d.ts +1 -1
  103. package/dist/runtime/index.js +9 -8
  104. package/dist/sdk/index.d.ts +3 -3
  105. package/dist/sdk/index.js +4 -3
  106. package/dist/sdk/index.js.map +1 -1
  107. package/dist/sdk/test.d.ts +2 -2
  108. package/dist/sdk/test.js +6 -5
  109. package/dist/sdk/test.js.map +1 -1
  110. package/dist/{serve-D3GO36UT.js → serve-GER7URPR.js} +11 -10
  111. package/dist/{serve-D3GO36UT.js.map → serve-GER7URPR.js.map} +1 -1
  112. package/dist/{status-KOSKCETC.js → status-LAJE3BM5.js} +7 -6
  113. package/dist/{status-KOSKCETC.js.map → status-LAJE3BM5.js.map} +1 -1
  114. package/dist/{statusline-ZNTCL5YC.js → statusline-L376ZPZU.js} +10 -9
  115. package/dist/{statusline-ZNTCL5YC.js.map → statusline-L376ZPZU.js.map} +1 -1
  116. package/dist/{telemetry-XIKUUGTQ.js → telemetry-B3DX3PWU.js} +6 -5
  117. package/dist/{telemetry-XIKUUGTQ.js.map → telemetry-B3DX3PWU.js.map} +1 -1
  118. package/dist/{trae-NWT3TMSZ.js → trae-7CZZGOGI.js} +3 -3
  119. package/dist/{types-BEKsAium.d.ts → types-CRVrFWeU.d.ts} +64 -22
  120. package/dist/{uninstall-2LBLQVFA.js → uninstall-ZYTW7YMZ.js} +11 -10
  121. package/dist/{uninstall-2LBLQVFA.js.map → uninstall-ZYTW7YMZ.js.map} +1 -1
  122. package/dist/{upgrade-ZC2S7VRN.js → upgrade-4LUEEOZW.js} +11 -10
  123. package/dist/{upgrade-ZC2S7VRN.js.map → upgrade-4LUEEOZW.js.map} +1 -1
  124. package/dist/{usage-E2A4O2CY.js → usage-ZFBVQF3H.js} +3 -3
  125. package/dist/{usage-event-JKMC4QC4.js → usage-event-GZ32AGEB.js} +10 -9
  126. package/dist/{usage-event-JKMC4QC4.js.map → usage-event-GZ32AGEB.js.map} +1 -1
  127. package/dist/{vscode-copilot-ALV45XF6.js → vscode-copilot-MF2CDNMT.js} +23 -10
  128. package/dist/vscode-copilot-MF2CDNMT.js.map +1 -0
  129. package/dist/{warp-H6IX4U3J.js → warp-VKG55TAT.js} +55 -4
  130. package/dist/warp-VKG55TAT.js.map +1 -0
  131. package/dist/windsurf-FU5G4SHF.js +443 -0
  132. package/dist/windsurf-FU5G4SHF.js.map +1 -0
  133. package/dist/{zed-RH34TNSW.js → zed-SFUHL3GS.js} +3 -3
  134. package/package.json +2 -2
  135. package/dist/chunk-3PFFTFCU.js +0 -190
  136. package/dist/chunk-3PFFTFCU.js.map +0 -1
  137. package/dist/chunk-76FUSVIA.js.map +0 -1
  138. package/dist/chunk-ADTHT2FN.js.map +0 -1
  139. package/dist/chunk-IW77FFNX.js.map +0 -1
  140. package/dist/chunk-KGGBNKI4.js.map +0 -1
  141. package/dist/chunk-MAH6QQTO.js.map +0 -1
  142. package/dist/claude-code-MLWS7CW6.js.map +0 -1
  143. package/dist/codebuff-SH2S3QAN.js.map +0 -1
  144. package/dist/codex-BXA4FIF4.js.map +0 -1
  145. package/dist/copilot-cli-ROIBTTDT.js.map +0 -1
  146. package/dist/crush-QLEFLHUC.js.map +0 -1
  147. package/dist/cursor-ASJPWZ4R.js.map +0 -1
  148. package/dist/doctor-TJUGSQ5C.js.map +0 -1
  149. package/dist/droid-NCSS3B5Y.js.map +0 -1
  150. package/dist/gemini-cli-SLVWLXJW.js.map +0 -1
  151. package/dist/goose-PBA7KTE5.js.map +0 -1
  152. package/dist/hermes-ONDCECP2.js.map +0 -1
  153. package/dist/hook-OU522ABV.js.map +0 -1
  154. package/dist/jetbrains-copilot-YABZ3HFA.js.map +0 -1
  155. package/dist/kilo-F3ZDZKCT.js.map +0 -1
  156. package/dist/kimi-NV72FVAO.js.map +0 -1
  157. package/dist/mimo-code-IMM5YM3M.js.map +0 -1
  158. package/dist/mux-DSSH4AFS.js.map +0 -1
  159. package/dist/omp-Z6FRNDPN.js.map +0 -1
  160. package/dist/opencode-Q6KBSAKW.js.map +0 -1
  161. package/dist/qwen-code-WQBKRPWJ.js.map +0 -1
  162. package/dist/vscode-copilot-ALV45XF6.js.map +0 -1
  163. package/dist/warp-H6IX4U3J.js.map +0 -1
  164. /package/dist/{amp-B524U6LO.js.map → amp-4U33CU7E.js.map} +0 -0
  165. /package/dist/{antigravity-ANDKF3K7.js.map → antigravity-AMKKKHS4.js.map} +0 -0
  166. /package/dist/{antigravity-cli-LLRPDDOT.js.map → antigravity-cli-XERXEH5W.js.map} +0 -0
  167. /package/dist/{chunk-KGJWE6A2.js.map → chunk-24IDL5RW.js.map} +0 -0
  168. /package/dist/{chunk-CIRDOY2V.js.map → chunk-2AVLX7GH.js.map} +0 -0
  169. /package/dist/{chunk-J3D5W5T2.js.map → chunk-EGCBZ2DC.js.map} +0 -0
  170. /package/dist/{chunk-22Y24V7O.js.map → chunk-F5RX62T7.js.map} +0 -0
  171. /package/dist/{chunk-LL4OBGRU.js.map → chunk-LQRAPXS4.js.map} +0 -0
  172. /package/dist/{chunk-G3ER4SY5.js.map → chunk-RIMVWCZY.js.map} +0 -0
  173. /package/dist/{chunk-6XHB6BE3.js.map → chunk-SCVBYSF7.js.map} +0 -0
  174. /package/dist/{kilo-cli-3HWPWBY4.js.map → kilo-cli-UZ7R5UOP.js.map} +0 -0
  175. /package/dist/{kiro-C5W6AGDR.js.map → kiro-YL6AWUYG.js.map} +0 -0
  176. /package/dist/{leaderboard-TIBVONVT.js.map → leaderboard-MJG24O35.js.map} +0 -0
  177. /package/dist/{nemoclaw-3EVNKD3I.js.map → nemoclaw-W5KEJ3WX.js.map} +0 -0
  178. /package/dist/{openclaw-7G437NT7.js.map → openclaw-EXJEFNZI.js.map} +0 -0
  179. /package/dist/{roo-code-GWSNU3OW.js.map → roo-code-67TYJIQR.js.map} +0 -0
  180. /package/dist/{trae-NWT3TMSZ.js.map → trae-7CZZGOGI.js.map} +0 -0
  181. /package/dist/{usage-E2A4O2CY.js.map → usage-ZFBVQF3H.js.map} +0 -0
  182. /package/dist/{zed-RH34TNSW.js.map → zed-SFUHL3GS.js.map} +0 -0
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  installInstructions,
4
4
  packageConnector
5
- } from "./chunk-MAH6QQTO.js";
5
+ } from "./chunk-QREL7LZJ.js";
6
6
  import {
7
7
  MARKETPLACE_NAME,
8
8
  agyPluginInstalled,
@@ -41,10 +41,10 @@ import {
41
41
  } from "./chunk-G5CQHVQG.js";
42
42
  import {
43
43
  spawnChild
44
- } from "./chunk-22Y24V7O.js";
44
+ } from "./chunk-F5RX62T7.js";
45
45
  import {
46
46
  detectInstalledPlatforms
47
- } from "./chunk-J3D5W5T2.js";
47
+ } from "./chunk-EGCBZ2DC.js";
48
48
  import {
49
49
  log
50
50
  } from "./chunk-KHO5WNTP.js";
@@ -52,14 +52,14 @@ import {
52
52
  deregisterConnector,
53
53
  loadRegisteredConnector,
54
54
  registerConnector
55
- } from "./chunk-KGJWE6A2.js";
56
- import {
57
- configPatchManualEdit
58
- } from "./chunk-ADTHT2FN.js";
55
+ } from "./chunk-24IDL5RW.js";
59
56
  import {
60
57
  REGISTERED_PLATFORM_IDS,
61
58
  loadAdapter
62
- } from "./chunk-3PFFTFCU.js";
59
+ } from "./chunk-DCKFYWGM.js";
60
+ import {
61
+ configPatchManualEdit
62
+ } from "./chunk-FAHIMJRU.js";
63
63
  import {
64
64
  hasMemoryLedger
65
65
  } from "./chunk-N6HH7K6P.js";
@@ -3032,4 +3032,4 @@ export {
3032
3032
  upgradeViaMarketplace,
3033
3033
  marketplaceDoctorChecks
3034
3034
  };
3035
- //# sourceMappingURL=chunk-G3ER4SY5.js.map
3035
+ //# sourceMappingURL=chunk-RIMVWCZY.js.map
@@ -354,4 +354,4 @@ export {
354
354
  hostLeaderboard,
355
355
  formatHostLeaderboard
356
356
  };
357
- //# sourceMappingURL=chunk-6XHB6BE3.js.map
357
+ //# sourceMappingURL=chunk-SCVBYSF7.js.map
@@ -5,11 +5,11 @@ import {
5
5
  import {
6
6
  BaseAdapter,
7
7
  parseJsonc
8
- } from "./chunk-IW77FFNX.js";
8
+ } from "./chunk-CPLK7GTE.js";
9
9
  import {
10
10
  buildServeWrapperCommand,
11
11
  shouldWrapForTelemetry
12
- } from "./chunk-BXGFUENQ.js";
12
+ } from "./chunk-I5MLE6S7.js";
13
13
 
14
14
  // src/adapters/openclaw/index.ts
15
15
  import {
@@ -81,14 +81,22 @@ var OpenClawAdapter = class extends BaseAdapter {
81
81
  canModifyOutput: false,
82
82
  // injected via before_prompt_build at prompt assembly.
83
83
  canInjectSessionContext: true,
84
- // OpenClaw's MCP sidecar registration is stdio-only in practice.
85
- transports: ["stdio"],
84
+ // stdio sidecars + remote servers. OpenClaw's validator accepts a remote
85
+ // transport of "sse" | "streamable-http" (AC "http" → "streamable-http");
86
+ // verified against OpenClaw 2026.6.1 + docs.openclaw.ai/gateway/configuration-reference.
87
+ transports: ["stdio", "sse", "http"],
86
88
  // Content surface: OpenClaw follows the AgentSkills spec (dir-per-skill
87
89
  // SKILL.md with {name, description} frontmatter + body). User scope →
88
90
  // ~/.openclaw/skills (the `openclaw skills install --global` target);
89
91
  // project scope → the resolved <workspace>/skills (the highest-priority
90
92
  // documented skill root, the same workspace the memory surface anchors to).
91
- supportsSkills: true
93
+ supportsSkills: true,
94
+ // Action surface: each declared action becomes a user-invokable slash
95
+ // command registered INSIDE the generated plugin module (api.registerCommand),
96
+ // shelling out to the home-bin action verb with the install target baked as
97
+ // the host token (this.id), so a fork like NemoClaw emits `action nemoclaw`.
98
+ // supportsActions -> the installActions/uninstallActions overrides below.
99
+ supportsActions: true
92
100
  };
93
101
  // ── Tolerant JSON5/JSONC read (override base strict JSON.parse) ──────────
94
102
  /**
@@ -225,7 +233,7 @@ var OpenClawAdapter = class extends BaseAdapter {
225
233
  * stdio → { command, args?, env? } (NO transport key — a stdio sidecar is
226
234
  * inferred from `command`; OpenClaw's validator rejects
227
235
  * transport:"stdio" and only accepts "sse"/"streamable-http")
228
- * remote → { url, transport: "sse"|"http", headers? }
236
+ * remote → { url, transport: "sse"|"streamable-http", headers? }
229
237
  *
230
238
  * OpenClaw documents no native ${env:VAR} token, so refs resolve to literals.
231
239
  */
@@ -255,7 +263,7 @@ var OpenClawAdapter = class extends BaseAdapter {
255
263
  if (server.enabled === false) entry2.enabled = false;
256
264
  return entry2;
257
265
  }
258
- const transport = server.transport === "http" ? "http" : "sse";
266
+ const transport = server.transport === "http" ? "streamable-http" : "sse";
259
267
  const entry = {
260
268
  url: resolveEnvRefsDeep(server.url ?? ""),
261
269
  transport
@@ -340,7 +348,6 @@ var OpenClawAdapter = class extends BaseAdapter {
340
348
  // ── Hook install / uninstall (ts-plugin + DUAL REGISTRATION) ────────────
341
349
  installHooks(ctx) {
342
350
  const pluginPath = this.getHookConfigPath(ctx);
343
- const configPath = this.getServerConfigPath(ctx);
344
351
  if (ctx.connector.platforms[this.id]?.hooks === false) {
345
352
  return [
346
353
  { platform: this.id, action: "skip", path: pluginPath, detail: `hooks disabled for ${this.id}` }
@@ -348,9 +355,64 @@ var OpenClawAdapter = class extends BaseAdapter {
348
355
  }
349
356
  if (ctx.connector.hookEvents.length === 0) {
350
357
  return [
351
- { platform: this.id, action: "skip", path: pluginPath, detail: "connector declares no hooks" }
358
+ {
359
+ platform: this.id,
360
+ action: "skip",
361
+ path: pluginPath,
362
+ detail: this.actionTriggers(ctx).length > 0 ? "no hooks (plugin written for actions)" : "connector declares no hooks"
363
+ }
364
+ ];
365
+ }
366
+ return this.ensurePlugin(ctx, `openclaw plugin module (${this.hookDetail(ctx)})`);
367
+ }
368
+ // ── Action install / uninstall (slash commands in the shared plugin) ────
369
+ /**
370
+ * Each declared action becomes an `api.registerCommand(...)` inside the SAME
371
+ * synthesized plugin module (see buildPluginSource). So installActions ensures
372
+ * that shared module is on disk AND dual-registered: when hooks already wrote
373
+ * the identical bytes the content-compare yields "skip" (no double material
374
+ * write), and an actions-only connector gets the plugin written + registered
375
+ * here. Honors platforms.<id>.actions === false and the empty-actions skip.
376
+ */
377
+ installActions(ctx) {
378
+ const pluginPath = this.getHookConfigPath(ctx);
379
+ if (ctx.connector.platforms[this.id]?.actions === false) {
380
+ return [
381
+ { platform: this.id, action: "skip", path: pluginPath, detail: `actions disabled for ${this.id}` }
352
382
  ];
353
383
  }
384
+ if (this.actionTriggers(ctx).length === 0) {
385
+ return [
386
+ { platform: this.id, action: "skip", path: pluginPath, detail: "connector declares no actions" }
387
+ ];
388
+ }
389
+ return this.ensurePlugin(ctx, "openclaw plugin module (action commands)");
390
+ }
391
+ /**
392
+ * Actions live INSIDE the shared plugin module, which uninstallHooks removes
393
+ * (file + manifest + dual-registration entry) unconditionally — it runs last
394
+ * in the teardown pass. Removing the plugin here too would race uninstallHooks,
395
+ * so this is a single informational skip.
396
+ */
397
+ uninstallActions(ctx) {
398
+ return [
399
+ {
400
+ platform: this.id,
401
+ action: "skip",
402
+ path: this.getHookConfigPath(ctx),
403
+ detail: "action commands are embedded in the plugin module; removed by uninstallHooks"
404
+ }
405
+ ];
406
+ }
407
+ /**
408
+ * Synthesize + content-compare-write the plugin module(s) AND upsert the dual-
409
+ * registration entry. Shared by installHooks and installActions: the generator
410
+ * is a pure function of ctx, so whichever surface runs second sees byte-
411
+ * identical files (and an already-present entry) and reports "skip" — no double
412
+ * material write. `moduleDetail` flavors the module ChangeRecord per surface.
413
+ */
414
+ ensurePlugin(ctx, moduleDetail) {
415
+ const configPath = this.getServerConfigPath(ctx);
354
416
  const changes = [];
355
417
  for (const file of this.synthesizePlugin(ctx)) {
356
418
  const before = existsSync(file.path) ? this.safeRead(file.path) : void 0;
@@ -367,7 +429,7 @@ var OpenClawAdapter = class extends BaseAdapter {
367
429
  platform: this.id,
368
430
  action,
369
431
  path: file.path,
370
- detail: `openclaw plugin module (${this.hookDetail(ctx)})`
432
+ detail: moduleDetail
371
433
  });
372
434
  }
373
435
  changes.push(this.upsertPluginEntry(configPath, ctx, this.pluginDir(ctx)));
@@ -652,7 +714,8 @@ var OpenClawAdapter = class extends BaseAdapter {
652
714
  const homeBin = JSON.stringify(ctx.homeBinPath);
653
715
  const connectorId = JSON.stringify(ctx.connector.id);
654
716
  const connectorName = JSON.stringify(ctx.connector.displayName || ctx.connector.id);
655
- const events = ctx.connector.hookEvents.filter(
717
+ const canonicalOff = ctx.connector.platforms[this.id]?.hooks === false;
718
+ const events = canonicalOff ? [] : ctx.connector.hookEvents.filter(
656
719
  (e) => EVENT_TO_OPENCLAW[e] !== void 0
657
720
  );
658
721
  const has = (e) => events.includes(e);
@@ -737,6 +800,20 @@ let contextInjected = false;
737
800
  '\n // SessionStart \u2192 session_start records the real session id and fetches\n // the additionalContext to inject; before_prompt_build performs the\n // injection (the verified context-injection point upstream).\n on("session_start", async (event) => {\n const e = event || {};\n SESSION_ID = e.sessionId || e.sessionKey || SESSION_ID;\n const res = bridge("SessionStart", {\n sessionId: SESSION_ID,\n source: e.resumedFrom ? "resume" : "startup",\n projectDir: PROJECT_DIR,\n });\n contextInjected = false;\n pendingContext =\n res && typeof res.additionalContext === "string" && res.additionalContext\n ? res.additionalContext\n : null;\n return undefined;\n });\n\n // Inject the session-start context once, at prompt-build time.\n on("before_prompt_build", () => {\n if (contextInjected || !pendingContext) return undefined;\n contextInjected = true;\n return { appendSystemContext: pendingContext };\n });\n'
738
801
  );
739
802
  }
803
+ for (const action of this.actionTriggers(ctx)) {
804
+ const id = JSON.stringify(action.id);
805
+ const description = JSON.stringify(action.description);
806
+ reg.push(
807
+ "\n // Action /" + action.id + " -> run the home-bin action verb directly.\n api.registerCommand({\n name: " + id + ",\n description: " + description + ',\n handler: async (_ctx) => {\n try {\n const args = ["action", ' + JSON.stringify(this.id) + ", " + id + `, "--connector", CONNECTOR_ID];
808
+ const out =
809
+ process.platform === "win32"
810
+ ? execSync([HOME_BIN, ...args].map((a) => '"' + a + '"').join(" "), { encoding: "utf8" })
811
+ : execFileSync(HOME_BIN, args, { encoding: "utf8" });
812
+ return { text: (out || "").trim() };
813
+ } catch (e) {
814
+ return { text: "action " + ` + id + ' + " failed: " + (e && e.message ? e.message : "error") };\n }\n },\n });\n'
815
+ );
816
+ }
740
817
  reg.push(" },\n");
741
818
  const definition = "const plugin = {\n id: CONNECTOR_ID,\n name: " + connectorName + ',\n configSchema: {\n type: "object",\n properties: {\n enabled: {\n type: "boolean",\n default: true,\n description: "Enable or disable the " + CONNECTOR_ID + " plugin.",\n },\n },\n additionalProperties: false,\n },\n' + reg.join("") + "};\n\nexport default plugin;\n";
742
819
  return header + definition;
@@ -820,7 +897,7 @@ let contextInjected = false;
820
897
  const pluginPath = this.getHookConfigPath(ctx);
821
898
  const pluginDir = this.pluginDir(ctx);
822
899
  const id = ctx.connector.id;
823
- const hasHooks = ctx.connector.hookEvents.length > 0;
900
+ const hasHooks = ctx.connector.hookEvents.length > 0 || this.actionTriggers(ctx).length > 0;
824
901
  const hasServer = Boolean(ctx.connector.server);
825
902
  return [
826
903
  {
@@ -937,4 +1014,4 @@ export {
937
1014
  adapter,
938
1015
  openclaw_default
939
1016
  };
940
- //# sourceMappingURL=chunk-KGGBNKI4.js.map
1017
+ //# sourceMappingURL=chunk-URHQMFB3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/openclaw/index.ts"],"sourcesContent":["/**\n * adapters/openclaw — OpenClaw (Gateway) platform adapter for agent-connector.\n *\n * OpenClaw is a **ts-plugin** host, but with a twist no other ts-plugin host\n * has: a plugin only delivers its MCP tools to the agent when it is registered\n * in TWO places at once (DUAL REGISTRATION):\n *\n * (a) plugins.entries.<id> = { enabled: true } + plugins.load.paths: [dir]\n * — so the gateway DISCOVERS and LOADS the plugin\n * module (runs register(api), wiring the hooks).\n * NOTE: the entry is { enabled } only — a per-entry\n * `module` field is rejected by `openclaw config\n * validate`, so discovery goes through the\n * plugins.load.paths dir scan instead; and\n * (b) mcp.servers.<id> — nested under a top-level \"mcp\" object, so the\n * plugin's MCP tools SURFACE to the agent.\n *\n * If only (a) is present, the plugin loads but ZERO tools reach the agent.\n * If only (b) is present, the tools are declared but the hook bridge never\n * loads. context-mode's own adapter proved this empirically against OpenClaw\n * 2026.4.22 (src/adapters/openclaw/index.ts checkPluginRegistration: \"in\n * plugins.entries but missing from mcp.servers — plugin loads but no ctx_*\n * tools reach the agent\"). So getHealthChecks() FAILS when the two are\n * inconsistent (entries XOR mcp.servers).\n *\n * Why we BRIDGE instead of importing handlers (mirrors the OpenCode adapter):\n * context-mode could load ITS OWN plugin in-process because the handler code\n * shipped with the package. agent-connector cannot: the connector's hook\n * handlers are arbitrary developer code we must not import into the gateway's\n * runtime (wrong cwd, wrong deps, version skew). So installHooks synthesizes a\n * tiny, fully self-contained ESM plugin module — { id, name, configSchema,\n * register(api) } — that imports NOTHING from agent-connector and, on each\n * hook firing, shells out to the ONE stable home binary's universal entrypoint\n * <homeBin> hook openclaw <event> --connector <id>\n * feeding the event payload on stdin and JSON.parsing the normalized\n * HookResponse back from stdout. Fail-open: any bridge error → no-op. The same\n * universal json-stdio dispatcher every other host uses thus serves OpenClaw.\n *\n * Plugin module shape (grounded in context-mode build/adapters/openclaw/plugin.js):\n * export default { id, name, configSchema, register(api) }\n * register(api) wires typed lifecycle hooks via api.on(event, handler):\n * before_tool_call → block (return { block, blockReason }) / rewrite args\n * (mutate event.params in place); deny+ask both block.\n * after_tool_call → observe (capture result/output).\n * session_start → record the real session id.\n * before_prompt_build → inject SessionStart additionalContext via\n * { appendSystemContext } (the verified context-injection\n * point; session_start itself returns no context payload).\n * subagent_spawned / subagent_ended → observe the subagent lifecycle\n * (SubagentStart / SubagentStop; observe-only — no\n * decision or context payload).\n * api.on() is the correct API for typed lifecycle hooks; api.registerHook() is\n * for generic command hooks. We register defensively (api.on present check).\n *\n * Config file (JSON5 / JSONC — PARSE TOLERANTLY):\n * user scope → resolveOpenClawConfigPath():\n * $OPENCLAW_CONFIG_PATH\n * else $OPENCLAW_STATE_DIR/openclaw.json\n * else ~/.openclaw/openclaw.json\n * (mirrors context-mode openclawConfigPath — the file the\n * gateway actually loads, NOT process.cwd()).\n * project scope → <projectDir>/openclaw.json (also supported).\n * openclaw.json may contain comments + trailing commas (JSON5), so we NEVER\n * use strict JSON.parse — readJson is overridden to strip comments/commas\n * first via the shared core/jsonc parseJsonc. writes are strict JSON\n * (idempotent, comment-free).\n *\n * Plugin module location (project | user):\n * user → <stateDir>/extensions/<id>/index.mjs (stateDir = dir of openclaw.json)\n * project → <projectDir>/.openclaw/extensions/<id>/index.mjs\n * Beside index.mjs we also emit an openclaw.plugin.json manifest. The plugin's\n * DIRECTORY (not the file) is added to plugins.load.paths so the gateway scans\n * it and loads the module; the plugins.entries.<id> = { enabled: true } half\n * then activates it. (There is no per-entry \"module\" field — validate rejects it.)\n *\n * The gateway hot-reloads its config on SIGUSR1 — no action needed here; the\n * next reload (or restart) picks up our entries/servers/module.\n *\n * Capabilities (per OpenClaw hook API):\n * preToolUse / postToolUse / sessionStart / subagentStart / subagentStop\n * true; transports [\"stdio\"].\n * before_tool_call mutates event.params → canModifyArgs true.\n * after_tool_call cannot rewrite the tool result → canModifyOutput false.\n * before_prompt_build injects context → canInjectSessionContext true.\n */\n\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type {\n Adapter,\n GeneratedPluginFile,\n HookReply,\n InstallContext,\n MemoryTarget,\n NormalizedEvent,\n} from \"../spi.js\";\nimport type {\n ChangeRecord,\n DetectedPlatform,\n HealthCheck,\n HookEventName,\n HookParadigm,\n HookResponse,\n PlatformCapabilities,\n PlatformId,\n PostToolUseEvent,\n PreToolUseEvent,\n ServerDef,\n SessionStartEvent,\n SkillDef,\n SubagentStartEvent,\n SubagentStopEvent,\n} from \"../../core/types.js\";\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport { parseJsonc } from \"../../core/jsonc.js\";\nimport {\n buildServeWrapperCommand,\n shouldWrapForTelemetry,\n} from \"../../core/spawn.js\";\n\nconst HOST: PlatformId = \"openclaw\";\n\n/**\n * MCP servers live nested under the top-level \"mcp\" object: mcp.servers.<id>\n * (NOT a top-level \"mcpServers\" key). plugins.entries.<id> is the second half of\n * the dual registration.\n */\nconst MCP_ROOT_KEY = \"mcp\";\nconst MCP_SERVERS_KEY = \"servers\";\nconst PLUGINS_KEY = \"plugins\";\nconst PLUGINS_ENTRIES_KEY = \"entries\";\n/**\n * The gateway discovers a synthesized plugin module by scanning the directories\n * listed in plugins.load.paths — it does NOT take a per-entry `module` field\n * (that fails `openclaw config validate`). We add the plugin's own dir here.\n */\nconst PLUGINS_LOAD_KEY = \"load\";\nconst PLUGINS_LOAD_PATHS_KEY = \"paths\";\n/** Manifest the gateway reads inside a plugins.load.paths directory. */\nconst PLUGIN_MANIFEST_FILE = \"openclaw.plugin.json\";\n\n/**\n * Canonical → OpenClaw event name map. A connector hook event is only emitted by\n * the generated plugin when it appears here AND is declared by the connector.\n *\n * SessionStart maps to OpenClaw's session_start (to learn the real session id)\n * plus before_prompt_build (the verified context-injection point — session_start\n * itself returns no context payload upstream).\n */\nconst EVENT_TO_OPENCLAW: Partial<Record<HookEventName, string>> = {\n PreToolUse: \"before_tool_call\",\n PostToolUse: \"after_tool_call\",\n SessionStart: \"session_start\",\n // Subagent lifecycle: OpenClaw observes launch + completion (no decision\n // control on either). PermissionRequest is deliberately ABSENT — OpenClaw's\n // permission gate is the requireApproval RETURN VALUE of before_tool_call,\n // not an observable event; PostToolUseFailure is ABSENT too (a failure\n // arrives merged into after_tool_call, already surfaced as PostToolUse\n // isError). Unmapped declared events are reported as \"unsupported here\".\n SubagentStart: \"subagent_spawned\",\n SubagentStop: \"subagent_ended\",\n};\n\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\ninterface OpenClawBridgePayload {\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolOutput?: string;\n isError?: boolean;\n sessionId?: string;\n source?: string;\n projectDir?: string;\n // subagent_spawned / subagent_ended (the generated plugin normalizes the\n // host's field-name variants before posting; empty strings are omitted).\n agentId?: string;\n agentType?: string;\n lastAssistantMessage?: string;\n}\n\n/**\n * Native MCP server entry shapes OpenClaw accepts under mcp.servers.<id>.\n *\n * A stdio sidecar carries NO `transport` field: OpenClaw's config validator\n * only accepts `transport: \"sse\" | \"streamable-http\"` for remote servers and\n * REJECTS `transport: \"stdio\"` (verified against OpenClaw 2026.6.1). A stdio\n * server is INFERRED from the presence of `command`, so we must never write a\n * transport key here.\n */\ninterface OpenClawStdioServer {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n enabled?: boolean;\n}\ninterface OpenClawRemoteServer {\n url: string;\n transport: \"sse\" | \"streamable-http\";\n headers?: Record<string, string>;\n enabled?: boolean;\n}\n\n/**\n * plugins.entries.<id> reference shape.\n *\n * `openclaw config validate` REJECTS a `module` field here (it is not part of\n * the entry schema), so the entry is `{ enabled: true }` ONLY. Discovery of the\n * synthesized module is wired separately via `plugins.load.paths` (the dir that\n * holds the module + its openclaw.plugin.json manifest).\n */\ninterface OpenClawPluginEntry {\n enabled: boolean;\n}\n\nexport class OpenClawAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n // Declared as `string` (not the literal \"OpenClaw\") so a fork adapter can\n // override it — NemoClaw extends this class (precedent: AntigravityAdapter,\n // whose name is likewise typed `string` for AntigravityCliAdapter).\n readonly name: string = \"OpenClaw\";\n readonly paradigm: HookParadigm = \"ts-plugin\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block. OpenClaw's AGENTS.md\n // lives in the AGENT WORKSPACE (default ~/.openclaw/workspace), NOT the\n // user's repo — memoryTargets below maps BOTH scopes there (R2:\n // platforms force scopes; personal-assistant semantics, no project file).\n supportsMemory: true,\n preToolUse: true,\n postToolUse: true,\n // OpenClaw compaction is owned by a context engine, not wired through the\n // universal bridge here.\n preCompact: false,\n sessionStart: true,\n sessionEnd: false,\n userPromptSubmit: false,\n stop: false,\n notification: false,\n // Newer events: OpenClaw observes the full subagent lifecycle via\n // subagent_spawned / subagent_ended (observe-only — no decision control,\n // so a SubagentStop \"deny\" cannot keep the subagent running here).\n // permissionRequest / postToolUseFailure stay unset: the permission gate\n // is a RETURN VALUE of before_tool_call (not an event) and tool failures\n // arrive merged into after_tool_call. Install reports the standard\n // \"unsupported here\" for unmapped declared events.\n subagentStart: true,\n subagentStop: true,\n // before_tool_call mutates event.params in place → input rewrite supported.\n canModifyArgs: true,\n // after_tool_call observes but cannot rewrite the tool result upstream.\n canModifyOutput: false,\n // injected via before_prompt_build at prompt assembly.\n canInjectSessionContext: true,\n // stdio sidecars + remote servers. OpenClaw's validator accepts a remote\n // transport of \"sse\" | \"streamable-http\" (AC \"http\" → \"streamable-http\");\n // verified against OpenClaw 2026.6.1 + docs.openclaw.ai/gateway/configuration-reference.\n transports: [\"stdio\", \"sse\", \"http\"],\n // Content surface: OpenClaw follows the AgentSkills spec (dir-per-skill\n // SKILL.md with {name, description} frontmatter + body). User scope →\n // ~/.openclaw/skills (the `openclaw skills install --global` target);\n // project scope → the resolved <workspace>/skills (the highest-priority\n // documented skill root, the same workspace the memory surface anchors to).\n supportsSkills: true,\n // Action surface: each declared action becomes a user-invokable slash\n // command registered INSIDE the generated plugin module (api.registerCommand),\n // shelling out to the home-bin action verb with the install target baked as\n // the host token (this.id), so a fork like NemoClaw emits `action nemoclaw`.\n // supportsActions -> the installActions/uninstallActions overrides below.\n supportsActions: true,\n };\n\n // ── Tolerant JSON5/JSONC read (override base strict JSON.parse) ──────────\n\n /**\n * openclaw.json is officially JSON5 (comments + trailing commas allowed). A\n * strict JSON.parse false-fails a perfectly valid commented file, so we strip\n * comments and trailing commas before parsing. Writes remain strict JSON\n * (writeJson in BaseAdapter), which is itself valid JSON5 — so round-trips\n * stay safe and idempotent.\n */\n protected override readJson<T = Record<string, unknown>>(path: string): T | null {\n if (!existsSync(path)) return null;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n try {\n // Shared, string-aware JSONC stripper (the local stripJsonish whose\n // trailing-comma regex corrupted in-string \",]\"/\",}\"-like values is gone).\n return parseJsonc<T>(raw);\n } catch {\n return null;\n }\n }\n\n // ── Memory surface: agent-workspace AGENTS.md (both scopes) ─────────────\n /**\n * OpenClaw loads AGENTS.md (operating instructions) from its AGENT WORKSPACE\n * every session — `agents.defaults.workspace` in openclaw.json when set,\n * else `<stateDir>/workspace` (default ~/.openclaw/workspace). That file is\n * personal-assistant state, NOT a repo file, so a \"project scope\" write does\n * not apply: BOTH scopes map to the workspace (R2: platforms force scopes),\n * with the reason string flagging the semantics.\n */\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\n if (this.memoryOverride(ctx)?.path) return super.memoryTargets(ctx);\n if (ctx.scope !== \"project\" && ctx.scope !== \"user\") return [];\n return [\n {\n path: join(this.resolveWorkspace(), \"AGENTS.md\"),\n reason:\n \"openclaw agent-workspace AGENTS.md (workspace-scoped operating instructions, \" +\n \"not the project repo — both scopes map here)\",\n },\n ];\n }\n\n /**\n * Resolve OpenClaw's AGENT WORKSPACE — `agents.defaults.workspace` in\n * openclaw.json when set, else `<stateDir>/workspace` (default\n * ~/.openclaw/workspace). The same root the memory surface and the\n * project-scope skills surface both anchor to, so they never diverge.\n */\n private resolveWorkspace(): string {\n const configPath = resolveOpenClawConfigPath();\n const cfg = this.readJson<{ agents?: { defaults?: { workspace?: unknown } } }>(configPath);\n const declared = cfg?.agents?.defaults?.workspace;\n return typeof declared === \"string\" && declared.trim() !== \"\"\n ? declared.startsWith(\"~\")\n ? resolve(homedir(), declared.replace(/^~[/\\\\]?/, \"\"))\n : resolve(declared)\n : join(dirname(configPath), \"workspace\");\n }\n\n // ── Detection ──────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userConfig = resolveOpenClawConfigPath();\n const userDir = join(homedir(), \".openclaw\");\n const projectConfig = join(projectDir, \"openclaw.json\");\n\n // Fork-before-parent at DETECTION: NemoClaw DRIVES ~/.openclaw/openclaw.json,\n // so on a NemoClaw box (its ~/.nemoclaw/ marker present) the user-scope\n // OpenClaw config is nemoclaw-OWNED — defer so the planner does not double-\n // target the shared file as two platforms (an `uninstall openclaw` would\n // otherwise strip the connector's entries out from under nemoclaw). The\n // NemoClaw adapter overrides detectInstalled, so it is unaffected by this.\n const deferToNemoClaw = existsSync(join(homedir(), \".nemoclaw\"));\n const userInstalled =\n !deferToNemoClaw && (existsSync(userConfig) || existsSync(userDir));\n const projInstalled = existsSync(projectConfig);\n const installed = userInstalled || projInstalled;\n\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projectConfig : userConfig;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? scope === \"project\"\n ? `found project OpenClaw config at ${projectConfig}`\n : `found OpenClaw config at ${configPath}`\n : `no OpenClaw config at ${userConfig} or ${userDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ───────────────────────────────────────────────────────\n\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? ctx.projectDir\n : dirname(resolveOpenClawConfigPath());\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \"openclaw.json\")\n : resolveOpenClawConfigPath();\n }\n\n /**\n * For ts-plugin hosts the \"hook config path\" is the generated plugin FILE.\n * OpenClaw additionally needs the plugins.entries reference written into\n * openclaw.json (handled in installHooks); this is the module on disk.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return join(this.pluginDir(ctx), this.pluginFileName());\n }\n\n /** Extensions directory the synthesized plugin module is written into. */\n private pluginDir(ctx: InstallContext): string {\n const base =\n ctx.scope === \"project\"\n ? join(ctx.projectDir, \".openclaw\")\n : dirname(resolveOpenClawConfigPath());\n return join(base, \"extensions\", ctx.connector.id);\n }\n\n /** Plugin module file name (ESM so the gateway loads it without a loader). */\n private pluginFileName(): string {\n return \"index.mjs\";\n }\n\n // ── MCP server install / uninstall (mcp.servers.<id>) ───────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const server = this.effectiveServer(ctx);\n const serverPath = this.getServerConfigPath(ctx);\n\n if (!server) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: serverPath,\n detail: ctx.connector.server\n ? \"server registration disabled for openclaw\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n const entry = this.renderServerEntry(ctx, server);\n return [this.upsertNestedServer(serverPath, ctx.connector.id, entry, ctx.dryRun)];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [this.removeNestedServer(serverPath, ctx.connector.id, ctx.dryRun)];\n }\n\n /** Resolve the per-platform server override into an effective ServerDef. */\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\n const override = ctx.connector.platforms[this.id]?.server;\n if (override === false) return undefined;\n const base = ctx.connector.server;\n if (!base) return undefined;\n return override && typeof override === \"object\" ? { ...base, ...override } : base;\n }\n\n /**\n * Render a normalized ServerDef into OpenClaw's native mcp.servers.<id> entry.\n *\n * stdio → { command, args?, env? } (NO transport key — a stdio sidecar is\n * inferred from `command`; OpenClaw's validator rejects\n * transport:\"stdio\" and only accepts \"sse\"/\"streamable-http\")\n * remote → { url, transport: \"sse\"|\"streamable-http\", headers? }\n *\n * OpenClaw documents no native ${env:VAR} token, so refs resolve to literals.\n */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): OpenClawStdioServer | OpenClawRemoteServer {\n if (server.transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\n if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {\n const wrapped = buildServeWrapperCommand(\n ctx.homeBinPath,\n ctx.connector.id,\n command,\n args,\n ctx.scope,\n this.id,\n );\n command = wrapped.command;\n args = wrapped.args;\n }\n\n // NO `transport` key: OpenClaw infers a stdio sidecar from `command` and\n // its validator rejects transport:\"stdio\" (only \"sse\"/\"streamable-http\").\n const entry: OpenClawStdioServer = {\n command: resolveEnvRefsDeep(command),\n };\n const resolvedArgs = resolveEnvRefsDeep(args).filter((s) => s !== \"\");\n if (resolvedArgs.length > 0) entry.args = resolvedArgs;\n const env = this.renderEnv(server.env);\n if (env) entry.env = env;\n if (server.enabled === false) entry.enabled = false;\n return entry;\n }\n\n // sse / http → OpenClaw registers a remote URL sidecar. AC's canonical\n // \"http\" (streamable HTTP) maps to OpenClaw's literal \"streamable-http\" — its\n // validator rejects a bare \"http\" transport value.\n const transport: \"sse\" | \"streamable-http\" =\n server.transport === \"http\" ? \"streamable-http\" : \"sse\";\n const entry: OpenClawRemoteServer = {\n url: resolveEnvRefsDeep(server.url ?? \"\"),\n transport,\n };\n if (server.headers && Object.keys(server.headers).length > 0) {\n entry.headers = resolveEnvRefsDeep({ ...server.headers });\n }\n if (server.enabled === false) entry.enabled = false;\n return entry;\n }\n\n /** Resolve every ${env:VAR} in env values to literals (no native token). */\n private renderEnv(\n env: Record<string, string> | undefined,\n ): Record<string, string> | undefined {\n if (!env || Object.keys(env).length === 0) return undefined;\n return resolveEnvRefsDeep({ ...env });\n }\n\n /** Upsert config.mcp.servers.<id> idempotently. */\n private upsertNestedServer(\n path: string,\n id: string,\n entry: unknown,\n dryRun: boolean,\n ): ChangeRecord {\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\n if (this.isPresentButUnparseable(path)) {\n return {\n platform: this.id,\n action: \"warn\",\n path,\n detail: `existing ${path} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, unknown>>(path) ?? {};\n const servers = this.nestedServersBucket(cfg);\n const before = JSON.stringify(servers[id]);\n const after = JSON.stringify(entry);\n let action: ChangeRecord[\"action\"];\n if (before === undefined) action = \"create\";\n else if (before === after) action = \"skip\";\n else action = \"update\";\n if (action !== \"skip\") {\n servers[id] = entry;\n this.writeJson(path, cfg, dryRun);\n }\n return {\n platform: this.id,\n action,\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n\n /** Remove config.mcp.servers.<id>. */\n private removeNestedServer(path: string, id: string, dryRun: boolean): ChangeRecord {\n const cfg = this.readJson<Record<string, unknown>>(path);\n const mcp = cfg?.[MCP_ROOT_KEY];\n const servers =\n mcp && typeof mcp === \"object\" && !Array.isArray(mcp)\n ? ((mcp as Record<string, unknown>)[MCP_SERVERS_KEY] as\n | Record<string, unknown>\n | undefined)\n : undefined;\n if (!cfg || !servers || !(id in servers)) {\n return {\n platform: this.id,\n action: \"skip\",\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id} absent`,\n };\n }\n delete servers[id];\n this.writeJson(path, cfg, dryRun);\n return {\n platform: this.id,\n action: \"remove\",\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n\n /** Get-or-create config.mcp.servers as a mutable object. */\n private nestedServersBucket(cfg: Record<string, unknown>): Record<string, unknown> {\n let mcp = cfg[MCP_ROOT_KEY];\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) {\n mcp = {};\n cfg[MCP_ROOT_KEY] = mcp;\n }\n const mcpObj = mcp as Record<string, unknown>;\n let servers = mcpObj[MCP_SERVERS_KEY];\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) {\n servers = {};\n mcpObj[MCP_SERVERS_KEY] = servers;\n }\n return servers as Record<string, unknown>;\n }\n\n // ── Hook install / uninstall (ts-plugin + DUAL REGISTRATION) ────────────\n\n installHooks(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n\n if (ctx.connector.platforms[this.id]?.hooks === false) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: `hooks disabled for ${this.id}` },\n ];\n }\n // Empty-events gate: the plugin module is SHARED with the action surface, so\n // a connector with actions-but-no-hooks still needs it written + registered —\n // but that is installActions' job (it calls ensurePlugin too). installHooks\n // only writes when there ARE hooks; its skip detail stays honest about why.\n if (ctx.connector.hookEvents.length === 0) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: pluginPath,\n detail:\n this.actionTriggers(ctx).length > 0\n ? \"no hooks (plugin written for actions)\"\n : \"connector declares no hooks\",\n },\n ];\n }\n\n return this.ensurePlugin(ctx, `openclaw plugin module (${this.hookDetail(ctx)})`);\n }\n\n // ── Action install / uninstall (slash commands in the shared plugin) ────\n\n /**\n * Each declared action becomes an `api.registerCommand(...)` inside the SAME\n * synthesized plugin module (see buildPluginSource). So installActions ensures\n * that shared module is on disk AND dual-registered: when hooks already wrote\n * the identical bytes the content-compare yields \"skip\" (no double material\n * write), and an actions-only connector gets the plugin written + registered\n * here. Honors platforms.<id>.actions === false and the empty-actions skip.\n */\n override installActions(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n if (ctx.connector.platforms[this.id]?.actions === false) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: `actions disabled for ${this.id}` },\n ];\n }\n if (this.actionTriggers(ctx).length === 0) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: \"connector declares no actions\" },\n ];\n }\n return this.ensurePlugin(ctx, \"openclaw plugin module (action commands)\");\n }\n\n /**\n * Actions live INSIDE the shared plugin module, which uninstallHooks removes\n * (file + manifest + dual-registration entry) unconditionally — it runs last\n * in the teardown pass. Removing the plugin here too would race uninstallHooks,\n * so this is a single informational skip.\n */\n override uninstallActions(ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: this.getHookConfigPath(ctx),\n detail: \"action commands are embedded in the plugin module; removed by uninstallHooks\",\n },\n ];\n }\n\n /**\n * Synthesize + content-compare-write the plugin module(s) AND upsert the dual-\n * registration entry. Shared by installHooks and installActions: the generator\n * is a pure function of ctx, so whichever surface runs second sees byte-\n * identical files (and an already-present entry) and reports \"skip\" — no double\n * material write. `moduleDetail` flavors the module ChangeRecord per surface.\n */\n private ensurePlugin(ctx: InstallContext, moduleDetail: string): ChangeRecord[] {\n const configPath = this.getServerConfigPath(ctx);\n const changes: ChangeRecord[] = [];\n\n // 1. Write the synthesized plugin module(s).\n for (const file of this.synthesizePlugin(ctx)) {\n const before = existsSync(file.path) ? this.safeRead(file.path) : undefined;\n let action: ChangeRecord[\"action\"];\n if (before === undefined) action = \"create\";\n else if (before === file.contents) action = \"skip\";\n else action = \"update\";\n\n if (action !== \"skip\" && !ctx.dryRun) {\n ensureDir(dirname(file.path));\n writeFileSync(file.path, file.contents, \"utf8\");\n chmodSync(file.path, file.executable ? 0o755 : 0o644);\n }\n changes.push({\n platform: this.id,\n action,\n path: file.path,\n detail: moduleDetail,\n });\n }\n\n // 2. DUAL REGISTRATION half (a): enable plugins.entries.<id> AND add the\n // plugin DIR to plugins.load.paths so the gateway discovers + loads the\n // module. (half (b), mcp.servers.<id>, is written by installServer — both\n // are required for tools to reach the agent.)\n changes.push(this.upsertPluginEntry(configPath, ctx, this.pluginDir(ctx)));\n\n return changes;\n }\n\n /**\n * Human-facing summary of which declared events the synthesized module ACTUALLY\n * wires. Only events present in EVENT_TO_OPENCLAW are mapped/wired; any declared\n * event with no OpenClaw mapping (e.g. UserPromptSubmit) is reported separately\n * as \"unsupported here\" so the detail never overstates coverage.\n */\n private hookDetail(ctx: InstallContext): string {\n const declared = ctx.connector.hookEvents;\n const mapped = declared.filter((e) => EVENT_TO_OPENCLAW[e] !== undefined);\n const unsupported = declared.filter((e) => EVENT_TO_OPENCLAW[e] === undefined);\n const base = mapped.join(\",\");\n return unsupported.length > 0\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\n : base;\n }\n\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n const configPath = this.getServerConfigPath(ctx);\n const changes: ChangeRecord[] = [];\n\n // 1. Remove the plugins.entries.<id> reference AND drop the plugin dir from\n // plugins.load.paths.\n changes.push(\n this.removePluginEntry(configPath, ctx.connector.id, this.pluginDir(ctx), ctx.dryRun),\n );\n\n // 2. Remove the plugin module + its manifest on disk.\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\n for (const [path, label] of [\n [pluginPath, \"openclaw plugin module\"],\n [manifestPath, \"openclaw plugin manifest\"],\n ] as const) {\n if (existsSync(path)) {\n if (!ctx.dryRun) rmSync(path, { force: true });\n changes.push({ platform: this.id, action: \"remove\", path, detail: label });\n } else {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path,\n detail: `no ${label} present`,\n });\n }\n }\n\n // 3. Drop the now-empty plugin dir (only if WE own its full contents).\n changes.push(this.removeDirIfEmpty(this.pluginDir(ctx), ctx.dryRun));\n\n return changes;\n }\n\n /**\n * Upsert plugins.entries.<id> = { enabled: true } AND add `pluginDir` to\n * plugins.load.paths idempotently.\n *\n * `openclaw config validate` rejects a per-entry `module` field, so the entry\n * carries ONLY `{ enabled: true }`. The gateway discovers the synthesized\n * module by scanning the directories in plugins.load.paths (the dir holding\n * index.mjs + openclaw.plugin.json), so the dir is added there.\n */\n private upsertPluginEntry(\n configPath: string,\n ctx: InstallContext,\n pluginDir: string,\n ): ChangeRecord {\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\n if (this.isPresentButUnparseable(configPath)) {\n return {\n platform: this.id,\n action: \"warn\",\n path: configPath,\n detail: `existing ${configPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, unknown>>(configPath) ?? {};\n const entries = this.pluginEntriesBucket(cfg);\n const id = ctx.connector.id;\n const desired: OpenClawPluginEntry = { enabled: true };\n const entryBefore = JSON.stringify(entries[id]);\n const entryAfter = JSON.stringify(desired);\n\n // plugins.load.paths: add the plugin dir if absent.\n const loadPaths = this.pluginLoadPathsBucket(cfg);\n const pathPresent = loadPaths.includes(pluginDir);\n\n const entryChanged = entryBefore !== entryAfter;\n const pathChanged = !pathPresent;\n let action: ChangeRecord[\"action\"];\n if (entryBefore === undefined) action = \"create\";\n else if (!entryChanged && !pathChanged) action = \"skip\";\n else action = \"update\";\n\n if (action !== \"skip\") {\n entries[id] = desired;\n if (!pathPresent) loadPaths.push(pluginDir);\n this.writeJson(configPath, cfg, ctx.dryRun);\n }\n return {\n platform: this.id,\n action,\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\n };\n }\n\n /** Remove plugins.entries.<id> AND drop `pluginDir` from plugins.load.paths. */\n private removePluginEntry(\n configPath: string,\n id: string,\n pluginDir: string,\n dryRun: boolean,\n ): ChangeRecord {\n const cfg = this.readJson<Record<string, unknown>>(configPath);\n const plugins = cfg?.[PLUGINS_KEY];\n const pluginsObj =\n plugins && typeof plugins === \"object\" && !Array.isArray(plugins)\n ? (plugins as Record<string, unknown>)\n : undefined;\n const entries = pluginsObj?.[PLUGINS_ENTRIES_KEY] as\n | Record<string, unknown>\n | undefined;\n const load = pluginsObj?.[PLUGINS_LOAD_KEY] as Record<string, unknown> | undefined;\n const loadPaths =\n load && Array.isArray(load[PLUGINS_LOAD_PATHS_KEY])\n ? (load[PLUGINS_LOAD_PATHS_KEY] as unknown[])\n : undefined;\n\n const entryPresent = Boolean(entries && id in entries);\n const pathIdx = loadPaths ? loadPaths.indexOf(pluginDir) : -1;\n\n if (!cfg || (!entryPresent && pathIdx < 0)) {\n return {\n platform: this.id,\n action: \"skip\",\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} absent`,\n };\n }\n if (entryPresent && entries) delete entries[id];\n if (loadPaths && pathIdx >= 0) loadPaths.splice(pathIdx, 1);\n this.writeJson(configPath, cfg, dryRun);\n return {\n platform: this.id,\n action: \"remove\",\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\n };\n }\n\n /** Get-or-create config.plugins.entries as a mutable object. */\n private pluginEntriesBucket(cfg: Record<string, unknown>): Record<string, unknown> {\n let plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\n plugins = {};\n cfg[PLUGINS_KEY] = plugins;\n }\n const pluginsObj = plugins as Record<string, unknown>;\n let entries = pluginsObj[PLUGINS_ENTRIES_KEY];\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) {\n entries = {};\n pluginsObj[PLUGINS_ENTRIES_KEY] = entries;\n }\n return entries as Record<string, unknown>;\n }\n\n /** Get-or-create config.plugins.load.paths as a mutable string array. */\n private pluginLoadPathsBucket(cfg: Record<string, unknown>): string[] {\n let plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\n plugins = {};\n cfg[PLUGINS_KEY] = plugins;\n }\n const pluginsObj = plugins as Record<string, unknown>;\n let load = pluginsObj[PLUGINS_LOAD_KEY];\n if (!load || typeof load !== \"object\" || Array.isArray(load)) {\n load = {};\n pluginsObj[PLUGINS_LOAD_KEY] = load;\n }\n const loadObj = load as Record<string, unknown>;\n const existing = loadObj[PLUGINS_LOAD_PATHS_KEY];\n // Normalize to a fresh string[] (drop any non-string a user may have placed\n // here) and write it back so the caller mutates the live array.\n const paths = Array.isArray(existing)\n ? existing.filter((p): p is string => typeof p === \"string\")\n : [];\n loadObj[PLUGINS_LOAD_PATHS_KEY] = paths;\n return paths;\n }\n\n // ── Content surface: skills (dir-per-skill SKILL.md) ────────────────────\n // CONTENT-ONLY: pure native-file writers, mirroring codex/claude-code\n // installSkills + renderSkill. No runtime dispatch, no home-bin pointer, no\n // telemetry wrap. Each write is idempotent (byte-identical → skip) via\n // writeContentFile and reversible via removeContentFile + removeDirIfEmpty\n // (never rm -rf a shared dir). Honors platforms[\"openclaw\"].skills === false.\n //\n // Native locations (AgentSkills spec — dir-per-skill SKILL.md + resources):\n // user → ~/.openclaw/skills/<name>/SKILL.md (`openclaw skills install\n // --global` targets this dir)\n // project → <workspace>/skills/<name>/SKILL.md (the highest-priority\n // documented skill root; <workspace> is the SAME root the memory\n // surface resolves, so both content surfaces stay consistent).\n\n /**\n * Per-skill directory for ctx.scope:\n * user → ~/.openclaw/skills/<name> (`--global` install target)\n * project → <workspace>/skills/<name> (resolved agent workspace —\n * consistent with this adapter's memoryTargets resolution).\n */\n private skillDir(ctx: InstallContext, name: string): string {\n const base =\n ctx.scope === \"project\"\n ? join(this.resolveWorkspace(), \"skills\")\n : join(dirname(resolveOpenClawConfigPath()), \"skills\");\n return join(base, name);\n }\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[this.id]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: `skills disabled for ${this.id}` }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), this.renderSkill(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\n // (config-time validation already rejects these, but never trust input).\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n // Remove only the files we wrote (SKILL.md + declared resources), then the\n // skill dir itself when we own its full contents.\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue; // never delete outside the skill dir\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents — never rm -rf a\n // dir that still holds user-added / sibling-tool / shared files.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n /**\n * Render a skill's SKILL.md: frontmatter (name, description + optional model,\n * allowed-tools, disable-model-invocation) + body. UNIFORM with codex /\n * claude-code — only the parent dir differs (single-line frontmatter keys;\n * OpenClaw follows the AgentSkills spec).\n */\n private renderSkill(skill: SkillDef): string {\n const frontmatter: Record<string, unknown> = {\n name: skill.name,\n description: skill.description,\n };\n if (skill.model !== undefined) frontmatter.model = skill.model;\n const allow = skill.tools?.allow;\n if (allow && allow.length > 0) frontmatter[\"allowed-tools\"] = allow.join(\", \");\n if (skill.disableModelInvocation !== undefined) {\n frontmatter[\"disable-model-invocation\"] = skill.disableModelInvocation;\n }\n if (skill.extra) Object.assign(frontmatter, skill.extra);\n return this.renderFrontmatterMd(frontmatter, skill.body);\n }\n\n // ── ts-plugin synthesis ────────────────────────────────────────────────\n\n /**\n * Build ONE self-contained ESM plugin module for OpenClaw.\n *\n * The module imports nothing from agent-connector. It embeds two constants\n * (the absolute home-bin path and the connector id) and a `bridge()` helper\n * that shells out to the universal hook entrypoint via execFileSync, feeding\n * the OpenClaw event payload on stdin and JSON.parsing the normalized\n * HookResponse back from stdout (fail-open: any error → null). Its default\n * export is the OpenClaw plugin definition { id, name, configSchema,\n * register(api) }; register wires api.on(...) handlers for the events this\n * connector actually declares.\n */\n synthesizePlugin(ctx: InstallContext): GeneratedPluginFile[] {\n const path = this.getHookConfigPath(ctx);\n const contents = this.buildPluginSource(ctx);\n // The gateway scans plugins.load.paths dirs for a manifest, so emit an\n // openclaw.plugin.json beside index.mjs pointing at the module entry.\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\n const manifest = this.buildPluginManifest(ctx);\n return [\n { path, contents, executable: false },\n { path: manifestPath, contents: manifest, executable: false },\n ];\n }\n\n /**\n * Build the openclaw.plugin.json manifest the gateway reads inside a\n * plugins.load.paths directory. It names the plugin and points at the ESM\n * module entry (index.mjs) so the loader knows what to import.\n */\n private buildPluginManifest(ctx: InstallContext): string {\n const manifest = {\n id: ctx.connector.id,\n name: ctx.connector.displayName || ctx.connector.id,\n main: this.pluginFileName(),\n enabled: true,\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n }\n\n /** Compose the generated plugin source with plain string concatenation. */\n private buildPluginSource(ctx: InstallContext): string {\n const homeBin = JSON.stringify(ctx.homeBinPath);\n const connectorId = JSON.stringify(ctx.connector.id);\n const connectorName = JSON.stringify(ctx.connector.displayName || ctx.connector.id);\n\n // `platforms[host].hooks === false` disables the CANONICAL events. The\n // plugin module may still be synthesized for ACTIONS (installActions writes\n // it independently of hooks), so the canonical handler set collapses to []\n // here rather than letting a live before_tool_call/after_tool_call gate leak\n // into a hooks-disabled plugin. Mirrors omp/opencode. Uses this.id so the\n // NemoClaw fork checks platforms.nemoclaw.hooks.\n const canonicalOff = ctx.connector.platforms[this.id]?.hooks === false;\n const events = canonicalOff\n ? []\n : ctx.connector.hookEvents.filter(\n (e): e is HookEventName => EVENT_TO_OPENCLAW[e] !== undefined,\n );\n const has = (e: HookEventName) => events.includes(e);\n\n const header =\n \"/**\\n\" +\n \" * AUTO-GENERATED by agent-connector — DO NOT EDIT.\\n\" +\n \" *\\n\" +\n \" * Self-contained OpenClaw plugin bridge for connector \" +\n ctx.connector.id +\n \".\\n\" +\n \" * It imports nothing from agent-connector: every hook invocation shells out\\n\" +\n \" * to the stable home binary's universal entrypoint and JSON-parses the\\n\" +\n \" * normalized response. Fail-open: any bridge error degrades to a no-op.\\n\" +\n \" *\\n\" +\n \" * Registered via DUAL REGISTRATION in openclaw.json: plugins.entries.<id>\\n\" +\n \" * (loads this module) + mcp.servers.<id> (surfaces the MCP tools). The\\n\" +\n \" * gateway hot-reloads its config on SIGUSR1.\\n\" +\n \" */\\n\" +\n 'import { execFileSync, execSync } from \"node:child_process\";\\n\\n' +\n \"const HOME_BIN = \" +\n homeBin +\n \";\\n\" +\n \"const CONNECTOR_ID = \" +\n connectorId +\n \";\\n\\n\" +\n \"/**\\n\" +\n \" * Invoke the universal hook entrypoint for one canonical event.\\n\" +\n \" * @param {string} event canonical event name\\n\" +\n \" * (PreToolUse|PostToolUse|SessionStart|SubagentStart|SubagentStop)\\n\" +\n \" * @param {object} payload OpenClaw-shaped payload posted on stdin\\n\" +\n \" * @returns {object|null} normalized HookResponse, or null on any failure\\n\" +\n \" */\\n\" +\n \"function bridge(event, payload) {\\n\" +\n \" try {\\n\" +\n \" // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\\n\" +\n \" // execFile a batch file, and shell+args is deprecated (DEP0190), so run\\n\" +\n \" // one quoted command line via a shell. POSIX keeps the direct execFile.\\n\" +\n ' const args = [\"hook\", ' + JSON.stringify(this.id) + ', event, \"--connector\", CONNECTOR_ID];\\n' +\n ' const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\\n' +\n \" const stdout =\\n\" +\n ' process.platform === \"win32\"\\n' +\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), opts)\\n\" +\n \" : execFileSync(HOME_BIN, args, opts);\\n\" +\n ' const text = (stdout || \"\").trim();\\n' +\n ' if (text === \"\") return { decision: \"allow\" };\\n' +\n \" return JSON.parse(text);\\n\" +\n \" } catch {\\n\" +\n \" // Fail-open — never wedge a tool call on a bridge error.\\n\" +\n \" return null;\\n\" +\n \" }\\n\" +\n \"}\\n\\n\" +\n \"// Module-scoped context the prompt-build hook injects at session start.\\n\" +\n \"// before_prompt_build is the verified injection point (session_start itself\\n\" +\n \"// returns no context payload). The flag prevents double-injection.\\n\" +\n \"let PROJECT_DIR = process.cwd();\\n\" +\n \"let SESSION_ID = \\\"\\\";\\n\" +\n \"let pendingContext = null;\\n\" +\n \"let contextInjected = false;\\n\\n\";\n\n // register(api) body — wire only the handlers this connector declares.\n const reg: string[] = [];\n\n reg.push(\n \" register(api) {\\n\" +\n \" PROJECT_DIR = process.cwd();\\n\" +\n \" const on = (event, handler) => {\\n\" +\n ' if (typeof api?.on === \"function\") {\\n' +\n \" try { api.on(event, handler); return; } catch { /* fall through */ }\\n\" +\n \" }\\n\" +\n ' if (typeof api?.registerHook === \"function\") {\\n' +\n \" try {\\n\" +\n ' api.registerHook(event, handler, { name: CONNECTOR_ID + \":\" + event, description: \"agent-connector bridge\" });\\n' +\n \" } catch { /* best effort */ }\\n\" +\n \" }\\n\" +\n \" };\\n\",\n );\n\n if (has(\"PreToolUse\")) {\n reg.push(\n \"\\n\" +\n \" // PreToolUse → before_tool_call: block (return { block, blockReason })\\n\" +\n \" // for deny/ask; rewrite args by mutating event.params in place for modify.\\n\" +\n ' on(\"before_tool_call\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const toolInput = e.params || {};\\n\" +\n \" const res = bridge(\\\"PreToolUse\\\", {\\n\" +\n ' toolName: e.toolName || \"\",\\n' +\n \" toolInput,\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" if (!res) return undefined;\\n\" +\n \" // OpenClaw has no separate ask gate — deny and ask both block (safe).\\n\" +\n ' if (res.decision === \"deny\" || res.decision === \"ask\") {\\n' +\n ' return { block: true, blockReason: res.reason || (\"Blocked by \" + CONNECTOR_ID) };\\n' +\n \" }\\n\" +\n ' if (res.decision === \"modify\" && res.updatedInput && typeof toolInput === \"object\") {\\n' +\n \" Object.assign(toolInput, res.updatedInput);\\n\" +\n \" }\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"PostToolUse\")) {\n reg.push(\n \"\\n\" +\n \" // PostToolUse → after_tool_call: observe the completed tool call.\\n\" +\n \" // OpenClaw exposes the result (v2+) or output (older builds); it cannot\\n\" +\n \" // rewrite the tool result, so updatedOutput is intentionally dropped.\\n\" +\n ' on(\"after_tool_call\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const rawResult = e.result !== undefined ? e.result : e.output;\\n\" +\n \" const toolOutput =\\n\" +\n ' typeof rawResult === \"string\"\\n' +\n \" ? rawResult\\n\" +\n \" : rawResult != null\\n\" +\n \" ? JSON.stringify(rawResult)\\n\" +\n \" : undefined;\\n\" +\n \" bridge(\\\"PostToolUse\\\", {\\n\" +\n ' toolName: e.toolName || \"\",\\n' +\n \" toolInput: e.params || {},\\n\" +\n \" toolOutput,\\n\" +\n \" isError: Boolean(e.error || e.isError),\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SubagentStart\")) {\n reg.push(\n \"\\n\" +\n \" // SubagentStart → subagent_spawned: observe-only (OpenClaw exposes no\\n\" +\n \" // decision or context-injection payload for subagent launches). Field\\n\" +\n \" // names vary across builds, so the common variants are normalized;\\n\" +\n \" // empty values are omitted so the dispatcher never matcher-filters on\\n\" +\n ' // an empty agent type (fail-open).\\n' +\n ' on(\"subagent_spawned\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" bridge(\\\"SubagentStart\\\", {\\n\" +\n \" agentId: e.agentId || e.subagentId || undefined,\\n\" +\n \" agentType: e.agentType || e.subagentType || e.agent || undefined,\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SubagentStop\")) {\n reg.push(\n \"\\n\" +\n \" // SubagentStop → subagent_ended: observe-only completion. A normalized\\n\" +\n \" // 'deny' cannot keep the subagent running on OpenClaw, so the bridge\\n\" +\n \" // reply is intentionally ignored.\\n\" +\n ' on(\"subagent_ended\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const rawResult = e.result !== undefined ? e.result : e.output;\\n\" +\n \" bridge(\\\"SubagentStop\\\", {\\n\" +\n \" agentId: e.agentId || e.subagentId || undefined,\\n\" +\n \" agentType: e.agentType || e.subagentType || e.agent || undefined,\\n\" +\n \" lastAssistantMessage:\\n\" +\n ' typeof rawResult === \"string\" && rawResult !== \"\" ? rawResult : undefined,\\n' +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SessionStart\")) {\n reg.push(\n \"\\n\" +\n \" // SessionStart → session_start records the real session id and fetches\\n\" +\n \" // the additionalContext to inject; before_prompt_build performs the\\n\" +\n \" // injection (the verified context-injection point upstream).\\n\" +\n ' on(\"session_start\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" SESSION_ID = e.sessionId || e.sessionKey || SESSION_ID;\\n\" +\n \" const res = bridge(\\\"SessionStart\\\", {\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n ' source: e.resumedFrom ? \"resume\" : \"startup\",\\n' +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" contextInjected = false;\\n\" +\n \" pendingContext =\\n\" +\n ' res && typeof res.additionalContext === \"string\" && res.additionalContext\\n' +\n \" ? res.additionalContext\\n\" +\n \" : null;\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\" +\n \"\\n\" +\n \" // Inject the session-start context once, at prompt-build time.\\n\" +\n ' on(\"before_prompt_build\", () => {\\n' +\n \" if (contextInjected || !pendingContext) return undefined;\\n\" +\n \" contextInjected = true;\\n\" +\n \" return { appendSystemContext: pendingContext };\\n\" +\n \" });\\n\",\n );\n }\n\n // Action surface: one api.registerCommand per declared action, registered in\n // the SAME register(api) body. The command shells out to the home-bin\n // `action <host>` verb with the host token baked as this.id (so a NemoClaw\n // fork emits `action nemoclaw`, NOT openclaw — exactly like the hook bridge\n // above). The win32 branch reuses the hook bridge's quoted-shell form (a .cmd\n // launcher throws EINVAL via execFileSync). Both id AND description are\n // JSON.stringify'd — descriptions are free-form connector text, so a raw \" /\n // backtick would break the generated module.\n for (const action of this.actionTriggers(ctx)) {\n const id = JSON.stringify(action.id);\n const description = JSON.stringify(action.description);\n reg.push(\n \"\\n\" +\n \" // Action /\" + action.id + \" -> run the home-bin action verb directly.\\n\" +\n \" api.registerCommand({\\n\" +\n \" name: \" + id + \",\\n\" +\n \" description: \" + description + \",\\n\" +\n \" handler: async (_ctx) => {\\n\" +\n \" try {\\n\" +\n ' const args = [\"action\", ' + JSON.stringify(this.id) + \", \" + id + ', \"--connector\", CONNECTOR_ID];\\n' +\n \" const out =\\n\" +\n ' process.platform === \"win32\"\\n' +\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), { encoding: \\\"utf8\\\" })\\n\" +\n ' : execFileSync(HOME_BIN, args, { encoding: \"utf8\" });\\n' +\n ' return { text: (out || \"\").trim() };\\n' +\n \" } catch (e) {\\n\" +\n \" return { text: \\\"action \\\" + \" + id + \" + \\\" failed: \\\" + (e && e.message ? e.message : \\\"error\\\") };\\n\" +\n \" }\\n\" +\n \" },\\n\" +\n \" });\\n\",\n );\n }\n\n reg.push(\" },\\n\");\n\n const definition =\n \"const plugin = {\\n\" +\n \" id: CONNECTOR_ID,\\n\" +\n \" name: \" +\n connectorName +\n \",\\n\" +\n \" configSchema: {\\n\" +\n ' type: \"object\",\\n' +\n \" properties: {\\n\" +\n \" enabled: {\\n\" +\n ' type: \"boolean\",\\n' +\n \" default: true,\\n\" +\n ' description: \"Enable or disable the \" + CONNECTOR_ID + \" plugin.\",\\n' +\n \" },\\n\" +\n \" },\\n\" +\n \" additionalProperties: false,\\n\" +\n \" },\\n\" +\n reg.join(\"\") +\n \"};\\n\\n\" +\n \"export default plugin;\\n\";\n\n return header + definition;\n }\n\n // ── Runtime: parse OUR bridge payload → normalized event ───────────────\n\n /**\n * `raw` is the payload OUR generated plugin posts (NOT a host-native shape):\n * { toolName, toolInput, toolOutput?, isError?, sessionId, source?, projectDir }\n * so this maps straight through.\n */\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\n const input = (raw ?? {}) as OpenClawBridgePayload;\n const base = {\n hostPlatform: this.id,\n connectorId: \"\",\n sessionId: typeof input.sessionId === \"string\" ? input.sessionId : \"\",\n raw,\n ...(typeof input.projectDir === \"string\" ? { projectDir: input.projectDir } : {}),\n } as const;\n\n switch (event) {\n case \"PreToolUse\": {\n const ev: PreToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n };\n return ev;\n }\n case \"PostToolUse\": {\n const ev: PostToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n ...(typeof input.toolOutput === \"string\" ? { toolOutput: input.toolOutput } : {}),\n ...(typeof input.isError === \"boolean\" ? { isError: input.isError } : {}),\n };\n return ev;\n }\n case \"SessionStart\": {\n const source: SessionStartEvent[\"source\"] =\n input.source === \"resume\"\n ? \"resume\"\n : input.source === \"compact\"\n ? \"compact\"\n : input.source === \"clear\"\n ? \"clear\"\n : \"startup\";\n const ev: SessionStartEvent = { ...base, source };\n return ev;\n }\n case \"SubagentStart\": {\n // Empty strings are dropped so the runtime matcher never filters a\n // real spawn on an unknown agent type (fail-open, per the dispatcher).\n const ev: SubagentStartEvent = {\n ...base,\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\n ...(isNonEmptyString(input.agentType)\n ? { agentType: input.agentType }\n : {}),\n };\n return ev;\n }\n case \"SubagentStop\": {\n const ev: SubagentStopEvent = {\n ...base,\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\n ...(isNonEmptyString(input.agentType)\n ? { agentType: input.agentType }\n : {}),\n ...(isNonEmptyString(input.lastAssistantMessage)\n ? { lastAssistantMessage: input.lastAssistantMessage }\n : {}),\n };\n return ev;\n }\n default:\n // Other canonical events are not surfaced by OpenClaw; treat as a\n // session-start-shaped no-op so the dispatcher fails open gracefully.\n return { ...base, source: \"startup\" } satisfies SessionStartEvent;\n }\n }\n\n // ── Runtime: normalized response → reply the generated bridge parses ───\n\n /**\n * Unlike json-stdio hosts (whose reply is the host's NATIVE control payload),\n * OUR generated bridge consumes this stdout directly. So the reply body is the\n * NORMALIZED HookResponse itself — the bridge JSON.parses it and reads\n * decision / updatedInput / additionalContext.\n */\n formatReply(_event: HookEventName, response: HookResponse): HookReply {\n return {\n exitCode: 0,\n stdout: JSON.stringify(response ?? { decision: \"allow\" }),\n };\n }\n\n // ── Diagnostics ────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const configPath = this.getServerConfigPath(ctx);\n const pluginPath = this.getHookConfigPath(ctx);\n const pluginDir = this.pluginDir(ctx);\n const id = ctx.connector.id;\n // Actions are health-equivalent to hooks: both write the SAME plugin module\n // and the SAME dual-registration entry, so an actions-only connector must\n // still assert the plugin/extension is present + loaded.\n const hasHooks =\n ctx.connector.hookEvents.length > 0 || this.actionTriggers(ctx).length > 0;\n const hasServer = Boolean(ctx.connector.server);\n\n return [\n {\n name: `${this.name}: openclaw.json present`,\n check: () =>\n existsSync(configPath)\n ? { status: \"OK\", detail: configPath }\n : { status: \"FAIL\", detail: `not found: ${configPath}` },\n },\n {\n // HARDEST PART — dual registration must be CONSISTENT. The plugin only\n // delivers tools when it is LOADED (plugins.entries.<id> enabled AND its\n // dir in plugins.load.paths) AND in mcp.servers. A config with one half\n // but not the other is a tool-less (or never-loaded) install, so FAIL on\n // loaded XOR mcp.servers. (We assert plugins.load.paths — NOT a per-entry\n // `module` field, which openclaw config validate rejects.)\n name: `${this.name}: dual registration (plugins.entries+load.paths + mcp.servers.${id})`,\n check: () => {\n // Only assert what the connector declares: a catalog-only connector\n // (no MCP server, no hooks) never writes either half of the dual\n // registration, so absence — even of the config file — is healthy.\n if (!hasServer && !hasHooks) {\n return { status: \"OK\", detail: \"no MCP server or hooks declared\" };\n }\n const cfg = this.readJson<Record<string, unknown>>(configPath);\n if (!cfg) return { status: \"FAIL\", detail: `cannot read ${configPath}` };\n\n const inEntries = this.hasPluginEntry(cfg, id) && this.hasLoadPath(cfg, pluginDir);\n const inMcp = this.hasMcpServer(cfg, id);\n\n // A hooks-only connector (no MCP server declared) does not need the\n // mcp.servers half — the load half alone is correct and complete.\n if (!hasServer) {\n return inEntries || !hasHooks\n ? { status: \"OK\", detail: \"hooks-only connector (no MCP server)\" }\n : {\n status: \"FAIL\",\n detail: `${id} not enabled in ${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} (plugin will not load)`,\n };\n }\n\n if (inEntries && inMcp) {\n return {\n status: \"OK\",\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id}+${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} + ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n if (inEntries && !inMcp) {\n return {\n status: \"FAIL\",\n detail: `loaded (${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}+${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) but missing from ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY} — plugin loads but no tools reach the agent`,\n };\n }\n if (!inEntries && inMcp) {\n return {\n status: \"FAIL\",\n detail: `in ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY} but not loaded (missing ${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY} or ${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) — tools declared but plugin never loads`,\n };\n }\n return {\n status: \"FAIL\",\n detail: `not loaded (${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}+${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) and not in ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}`,\n };\n },\n },\n {\n name: `${this.name}: plugin module present`,\n check: () => {\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\n return existsSync(pluginPath)\n ? { status: \"OK\", detail: pluginPath }\n : { status: \"FAIL\", detail: `not found: ${pluginPath}` };\n },\n },\n ];\n }\n\n /** True when config.plugins.entries.<id> exists (and is not disabled). */\n private hasPluginEntry(cfg: Record<string, unknown>, id: string): boolean {\n const plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n const entries = (plugins as Record<string, unknown>)[PLUGINS_ENTRIES_KEY];\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) return false;\n const entry = (entries as Record<string, unknown>)[id];\n if (entry === undefined) return false;\n // An explicit { enabled: false } means the gateway will not load it.\n return !(\n entry &&\n typeof entry === \"object\" &&\n (entry as Record<string, unknown>).enabled === false\n );\n }\n\n /** True when config.plugins.load.paths includes `pluginDir`. */\n private hasLoadPath(cfg: Record<string, unknown>, pluginDir: string): boolean {\n const plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n const load = (plugins as Record<string, unknown>)[PLUGINS_LOAD_KEY];\n if (!load || typeof load !== \"object\" || Array.isArray(load)) return false;\n const paths = (load as Record<string, unknown>)[PLUGINS_LOAD_PATHS_KEY];\n return Array.isArray(paths) && paths.includes(pluginDir);\n }\n\n /** True when config.mcp.servers.<id> exists. */\n private hasMcpServer(cfg: Record<string, unknown>, id: string): boolean {\n const mcp = cfg[MCP_ROOT_KEY];\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) return false;\n const servers = (mcp as Record<string, unknown>)[MCP_SERVERS_KEY];\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) return false;\n return id in (servers as Record<string, unknown>);\n }\n\n /** Read a file, returning undefined on any error (idempotency compare). */\n private safeRead(path: string): string | undefined {\n try {\n return readFileSync(path, \"utf8\");\n } catch {\n return undefined;\n }\n }\n}\n\n/**\n * Resolve the openclaw.json the gateway actually loads (mirrors context-mode's\n * openclawConfigPath): $OPENCLAW_CONFIG_PATH, else $OPENCLAW_STATE_DIR/openclaw.json,\n * else ~/.openclaw/openclaw.json. The gateway never loads a CWD/project-local\n * openclaw.json for user scope, so this is the canonical user-scope path.\n */\nfunction resolveOpenClawConfigPath(env: NodeJS.ProcessEnv = process.env): string {\n if (env.OPENCLAW_CONFIG_PATH) return resolve(env.OPENCLAW_CONFIG_PATH);\n const stateDir = env.OPENCLAW_STATE_DIR\n ? resolve(env.OPENCLAW_STATE_DIR)\n : resolve(homedir(), \".openclaw\");\n return resolve(stateDir, \"openclaw.json\");\n}\n\n/** Create a directory (recursive) if it does not already exist. */\nfunction ensureDir(dir: string): void {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\n/** Narrow to a non-empty string (the bridge omits empties, but stay defensive). */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\";\n}\n\nexport const adapter = new OpenClawAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/adapters/claude-code/wire.ts
4
+ function extractSessionId(input) {
5
+ if (typeof input.transcript_path === "string") {
6
+ const m = input.transcript_path.match(/([a-f0-9-]{36})\.jsonl$/);
7
+ if (m && m[1]) return m[1];
8
+ }
9
+ if (typeof input.session_id === "string" && input.session_id !== "") {
10
+ return input.session_id;
11
+ }
12
+ return "";
13
+ }
14
+ function toolResponseToString(value) {
15
+ if (value == null) return void 0;
16
+ if (typeof value === "string") return value;
17
+ try {
18
+ return JSON.stringify(value);
19
+ } catch {
20
+ return String(value);
21
+ }
22
+ }
23
+
24
+ export {
25
+ extractSessionId,
26
+ toolResponseToString
27
+ };
28
+ //# sourceMappingURL=chunk-WZA4CSGJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/claude-code/wire.ts"],"sourcesContent":["/**\n * adapters/claude-code/wire — Claude Code hook wire protocol.\n *\n * Claude Code pipes a JSON object to a hook command on stdin and reads a JSON\n * object (or empty) from stdout, interpreting the exit code as well. This module\n * captures the exact stdin shape and the canonical reply shape so the adapter's\n * parse/format methods are a single source of truth.\n *\n * Stdin (documented Claude Code hook input):\n * {\n * session_id, transcript_path, cwd, hook_event_name,\n * tool_name, tool_input, tool_response, // tool events\n * permission_suggestions, // PermissionRequest\n * tool_use_id, error, is_interrupt, duration_ms, // PostToolUseFailure\n * agent_id, agent_type, // SubagentStart/Stop\n * agent_transcript_path, last_assistant_message, // SubagentStop\n * source, // SessionStart\n * reason, // SessionEnd\n * prompt, // UserPromptSubmit\n * trigger, // PreCompact\n * stop_hook_active, // Stop / SubagentStop\n * message // Notification\n * }\n *\n * Stdout (canonical reply): a `hookSpecificOutput` object keyed by\n * `hookEventName`, carrying `permissionDecision` (allow|deny|ask) +\n * `permissionDecisionReason`, `additionalContext`, and (PreToolUse)\n * `updatedInput`; PermissionRequest instead takes a nested\n * `decision: { behavior: \"allow\"|\"deny\", ... }`; Stop-class blocks\n * (Stop / SubagentStop / UserPromptSubmit / PostToolUse) take the TOP-LEVEL\n * `{\"decision\":\"block\",\"reason\"}`. Exit 0 = proceed; the JSON refines the\n * decision.\n */\n\n/** Canonical Claude Code hook event names (the values of `hook_event_name`). */\nexport const CLAUDE_HOOK_EVENTS = [\n \"PreToolUse\",\n \"PostToolUse\",\n \"PreCompact\",\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"Stop\",\n \"Notification\",\n \"PermissionRequest\",\n \"PostToolUseFailure\",\n \"SubagentStart\",\n \"SubagentStop\",\n] as const;\n\nexport type ClaudeHookEvent = (typeof CLAUDE_HOOK_EVENTS)[number];\n\n/**\n * Raw Claude Code hook stdin payload. Every field is optional — a given event\n * only populates the subset relevant to it. `connector` is NOT a Claude field;\n * it is threaded in by the universal entrypoint so parseEvent can recover the\n * connector id (the home binary passes `--connector <id>` and may inject it).\n */\nexport interface ClaudeWireInput {\n session_id?: string;\n transcript_path?: string;\n cwd?: string;\n hook_event_name?: string;\n\n // tool events\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n /** PostToolUse result payload (string or structured). */\n tool_response?: unknown;\n\n // PermissionRequest — permission-update entries the dialog would offer.\n // NOTE: the docs explicitly state PermissionRequest has NO tool_use_id.\n permission_suggestions?: unknown[];\n\n // PostToolUseFailure\n tool_use_id?: string;\n error?: string;\n is_interrupt?: boolean;\n duration_ms?: number;\n\n // SubagentStart / SubagentStop — agent_type is unreliable on SubagentStop\n // (the SDK may not populate it); treat both as optional everywhere.\n agent_id?: string;\n agent_type?: string;\n // SubagentStop — the subagent's OWN transcript (transcript_path stays the\n // parent session's) + the text of its final response.\n agent_transcript_path?: string;\n last_assistant_message?: string;\n\n // SessionStart\n source?: string;\n // SessionEnd\n reason?: string;\n // UserPromptSubmit\n prompt?: string;\n // PreCompact\n trigger?: string;\n // Stop / SubagentStop\n stop_hook_active?: boolean;\n // Notification\n message?: string;\n\n /** Injected by the entrypoint so the runtime knows which connector to dispatch. */\n connector?: string;\n}\n\n/**\n * Extract a stable session id from a Claude wire payload.\n * Priority mirrors the proven adapter: transcript UUID > session_id > \"\".\n * (The framework's normalized event uses \"\" when no id is available, per\n * core/types BaseEvent.sessionId docstring — no ppid fabrication here.)\n */\nexport function extractSessionId(input: ClaudeWireInput): string {\n if (typeof input.transcript_path === \"string\") {\n const m = input.transcript_path.match(/([a-f0-9-]{36})\\.jsonl$/);\n if (m && m[1]) return m[1];\n }\n if (typeof input.session_id === \"string\" && input.session_id !== \"\") {\n return input.session_id;\n }\n return \"\";\n}\n\n/** Coerce a Claude PostToolUse `tool_response` into a string for the normalized event. */\nexport function toolResponseToString(value: unknown): string | undefined {\n if (value == null) return undefined;\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":";;;AAgHO,SAAS,iBAAiB,OAAgC;AAC/D,MAAI,OAAO,MAAM,oBAAoB,UAAU;AAC7C,UAAM,IAAI,MAAM,gBAAgB,MAAM,yBAAyB;AAC/D,QAAI,KAAK,EAAE,CAAC,EAAG,QAAO,EAAE,CAAC;AAAA,EAC3B;AACA,MAAI,OAAO,MAAM,eAAe,YAAY,MAAM,eAAe,IAAI;AACnE,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;AAGO,SAAS,qBAAqB,OAAoC;AACvE,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  allAdapters,
4
4
  loadAdapter
5
- } from "./chunk-3PFFTFCU.js";
5
+ } from "./chunk-DCKFYWGM.js";
6
6
 
7
7
  // src/sdk/introspect.ts
8
8
  var SURFACE_PREDICATES = {
@@ -13,9 +13,10 @@ var SURFACE_PREDICATES = {
13
13
  subagents: (c) => c.supportsSubagents ?? false,
14
14
  memory: (c) => c.supportsMemory ?? false,
15
15
  statusline: (c) => c.supportsStatusline ?? false,
16
- // v1: no adapter sets supportsActions (no affordance emitter yet), so this is
17
- // false everywhere and explain() marks actions skip-warn on every host the
18
- // honest state until the affordance emitter ships.
16
+ // Emitters ship on droid + hermes + warp plus the ts-plugin slash-command
17
+ // hosts omp + openclaw (+ the nemoclaw fork) the hosts with a verifiable
18
+ // target. Every other host leaves supportsActions unset, so explain() marks
19
+ // actions skip-warn there — the honest state for a host with no emission target.
19
20
  actions: (c) => c.supportsActions ?? false,
20
21
  configPatch: (c) => c.supportsConfigPatch ?? false,
21
22
  nativeHooks: (c) => c.supportsNativeHooks ?? false
@@ -40,4 +41,4 @@ export {
40
41
  hostsSupporting,
41
42
  surfaceSupport
42
43
  };
43
- //# sourceMappingURL=chunk-76FUSVIA.js.map
44
+ //# sourceMappingURL=chunk-XJ4S47CT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sdk/introspect.ts"],"sourcesContent":["/**\n * sdk/introspect — host-capability introspection over the adapter registry.\n *\n * A thin, READ-ONLY query layer above {@link loadAdapter} / {@link allAdapters}:\n * \"which hosts can honor surface X?\" and \"what can host Y do?\". It exists so a\n * connector author (or a docs/CLI surface) can answer those questions WITHOUT\n * reaching into adapter internals or re-deriving the per-surface capability\n * rules — the predicates below are the single source of truth for mapping the\n * raw {@link PlatformCapabilities} flags onto the developer-facing surface names.\n *\n * Everything is async because adapters load lazily (one entry per platform in\n * the registry, imported only when queried).\n */\n\nimport type { PlatformCapabilities, PlatformId } from \"../core/types.js\";\nimport { allAdapters, loadAdapter } from \"../adapters/registry.js\";\n\n/**\n * The developer-facing surface names a connector can declare. These mirror the\n * top-level {@link import(\"../core/types.js\").ConnectorConfig} keys (server,\n * hooks, commands, …) plus the platform-scoped `configPatch` / `nativeHooks`\n * escape hatches — the same vocabulary the install diff and `explain` use.\n */\nexport type SurfaceName =\n | \"server\"\n | \"hooks\"\n | \"commands\"\n | \"skills\"\n | \"subagents\"\n | \"memory\"\n | \"statusline\"\n | \"actions\"\n | \"configPatch\"\n | \"nativeHooks\";\n\n/**\n * Pure capability predicate per surface — `true` when the host can NATIVELY\n * honor that surface. The optional capability flags are read as `?? false`\n * (the supportsCommands precedent in core/types), so a host that simply leaves\n * a flag unset reports the surface as unsupported (the installer skip-warns it).\n *\n * `hooks` is satisfied by ANY normalized hook event (i.e. the host has at least\n * one event it can dispatch); the per-event flags map 1:1 onto the\n * {@link import(\"../core/types.js\").HookEventName} union.\n */\nexport const SURFACE_PREDICATES: Record<\n SurfaceName,\n (c: PlatformCapabilities) => boolean\n> = {\n server: (c) => c.transports.length > 0,\n hooks: (c) =>\n c.sessionStart ||\n c.preToolUse ||\n c.postToolUse ||\n c.userPromptSubmit ||\n c.stop ||\n c.sessionEnd ||\n c.preCompact ||\n c.notification ||\n (c.permissionRequest ?? false) ||\n (c.postToolUseFailure ?? false) ||\n (c.subagentStart ?? false) ||\n (c.subagentStop ?? false),\n commands: (c) => c.supportsCommands ?? false,\n skills: (c) => c.supportsSkills ?? false,\n subagents: (c) => c.supportsSubagents ?? false,\n memory: (c) => c.supportsMemory ?? false,\n statusline: (c) => c.supportsStatusline ?? false,\n // Emitters ship on droid + hermes + warp plus the ts-plugin slash-command\n // hosts omp + openclaw (+ the nemoclaw fork) — the hosts with a verifiable\n // target. Every other host leaves supportsActions unset, so explain() marks\n // actions skip-warn there — the honest state for a host with no emission target.\n actions: (c) => c.supportsActions ?? false,\n configPatch: (c) => c.supportsConfigPatch ?? false,\n nativeHooks: (c) => c.supportsNativeHooks ?? false,\n};\n\n/**\n * The {@link PlatformCapabilities} a host advertises, or `undefined` for an id\n * that is not in the registry (`\"unknown\"`, a usage-only id like `\"synthetic\"`,\n * or a typo) — callers branch on undefined rather than crash.\n */\nexport async function capabilitiesOf(\n host: PlatformId | string,\n): Promise<PlatformCapabilities | undefined> {\n const adapter = await loadAdapter(host);\n return adapter?.capabilities;\n}\n\n/**\n * Every registered host that can NATIVELY honor `surface`, as a sorted id list\n * (stable output for snapshots/docs). Loads all adapters in parallel and keeps\n * only those whose capabilities pass {@link SURFACE_PREDICATES}.\n */\nexport async function hostsSupporting(surface: SurfaceName): Promise<PlatformId[]> {\n const predicate = SURFACE_PREDICATES[surface];\n const adapters = await allAdapters();\n return adapters\n .filter((adapter) => predicate(adapter.capabilities))\n .map((adapter) => adapter.id)\n .sort();\n}\n\n/**\n * Convenience: does `host` natively support `surface`? `false` for an unknown\n * id (no capabilities → cannot support anything).\n */\nexport async function surfaceSupport(\n host: PlatformId | string,\n surface: SurfaceName,\n): Promise<boolean> {\n const capabilities = await capabilitiesOf(host);\n return capabilities ? SURFACE_PREDICATES[surface](capabilities) : false;\n}\n"],"mappings":";;;;;;;AA6CO,IAAM,qBAGT;AAAA,EACF,QAAQ,CAAC,MAAM,EAAE,WAAW,SAAS;AAAA,EACrC,OAAO,CAAC,MACN,EAAE,gBACF,EAAE,cACF,EAAE,eACF,EAAE,oBACF,EAAE,QACF,EAAE,cACF,EAAE,cACF,EAAE,iBACD,EAAE,qBAAqB,WACvB,EAAE,sBAAsB,WACxB,EAAE,iBAAiB,WACnB,EAAE,gBAAgB;AAAA,EACrB,UAAU,CAAC,MAAM,EAAE,oBAAoB;AAAA,EACvC,QAAQ,CAAC,MAAM,EAAE,kBAAkB;AAAA,EACnC,WAAW,CAAC,MAAM,EAAE,qBAAqB;AAAA,EACzC,QAAQ,CAAC,MAAM,EAAE,kBAAkB;AAAA,EACnC,YAAY,CAAC,MAAM,EAAE,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3C,SAAS,CAAC,MAAM,EAAE,mBAAmB;AAAA,EACrC,aAAa,CAAC,MAAM,EAAE,uBAAuB;AAAA,EAC7C,aAAa,CAAC,MAAM,EAAE,uBAAuB;AAC/C;AAOA,eAAsB,eACpB,MAC2C;AAC3C,QAAM,UAAU,MAAM,YAAY,IAAI;AACtC,SAAO,SAAS;AAClB;AAOA,eAAsB,gBAAgB,SAA6C;AACjF,QAAM,YAAY,mBAAmB,OAAO;AAC5C,QAAM,WAAW,MAAM,YAAY;AACnC,SAAO,SACJ,OAAO,CAAC,YAAY,UAAU,QAAQ,YAAY,CAAC,EACnD,IAAI,CAAC,YAAY,QAAQ,EAAE,EAC3B,KAAK;AACV;AAMA,eAAsB,eACpB,MACA,SACkB;AAClB,QAAM,eAAe,MAAM,eAAe,IAAI;AAC9C,SAAO,eAAe,mBAAmB,OAAO,EAAE,YAAY,IAAI;AACpE;","names":[]}