@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
@@ -4,12 +4,12 @@ import {
4
4
  } from "./chunk-DBQMX75U.js";
5
5
  import {
6
6
  BaseAdapter
7
- } from "./chunk-IW77FFNX.js";
7
+ } from "./chunk-CPLK7GTE.js";
8
8
  import "./chunk-7L6ITC4T.js";
9
9
  import {
10
10
  buildServeWrapperCommand,
11
11
  shouldWrapForTelemetry
12
- } from "./chunk-BXGFUENQ.js";
12
+ } from "./chunk-I5MLE6S7.js";
13
13
  import "./chunk-N6HH7K6P.js";
14
14
  import "./chunk-TRQKHP7D.js";
15
15
 
@@ -63,8 +63,21 @@ var OMPAdapter = class extends BaseAdapter {
63
63
  canModifyOutput: false,
64
64
  // No session_start context-injection surface upstream.
65
65
  canInjectSessionContext: false,
66
- // OMP registers stdio and remote (http) MCP servers.
67
- transports: ["stdio", "http"]
66
+ // OMP registers stdio and remote (streamable http + sse) MCP servers.
67
+ transports: ["stdio", "sse", "http"],
68
+ // Action surface: each declared action becomes a user-invokable slash
69
+ // command registered INSIDE the generated plugin module (pi.registerCommand),
70
+ // shelling out to the home-bin `action` verb. supportsActions → the
71
+ // installActions/uninstallActions overrides below.
72
+ supportsActions: true,
73
+ // Native passthrough hooks: OMP's main-loop lifecycle events —
74
+ // agent_start, agent_end, turn_start, turn_end — have NO canonical
75
+ // HookEventName analog (verified in oh-my-pi shared-events.ts). A connector
76
+ // reaches them by declaring platforms["omp"].nativeHooks; the generated
77
+ // plugin registers + bridges each declared native event verbatim
78
+ // (buildPluginSource native loop), and the home-bin runtime dispatches it
79
+ // host-generically via runNativeHook.
80
+ supportsNativeHooks: true
68
81
  };
69
82
  // ── Native paths ───────────────────────────────────────────────────────
70
83
  /**
@@ -132,8 +145,17 @@ var OMPAdapter = class extends BaseAdapter {
132
145
  }
133
146
  ];
134
147
  }
148
+ const changes = [];
149
+ if (!this.capabilities.transports.includes(server.transport)) {
150
+ changes.push({
151
+ platform: this.id,
152
+ action: "warn",
153
+ path: serverPath,
154
+ detail: `transport "${server.transport}" is not an OMP MCP transport (stdio/sse/http); registered best-effort as type:"http"`
155
+ });
156
+ }
135
157
  const entry = this.renderServerEntry(ctx, server);
136
- return [
158
+ changes.push(
137
159
  this.upsertServerInJson(
138
160
  serverPath,
139
161
  MCP_ROOT_KEY,
@@ -141,7 +163,8 @@ var OMPAdapter = class extends BaseAdapter {
141
163
  entry,
142
164
  ctx.dryRun
143
165
  )
144
- ];
166
+ );
167
+ return changes;
145
168
  }
146
169
  uninstallServer(ctx) {
147
170
  const serverPath = this.getServerConfigPath(ctx);
@@ -165,8 +188,10 @@ var OMPAdapter = class extends BaseAdapter {
165
188
  /**
166
189
  * Render a normalized ServerDef into OMP's native "mcpServers" entry.
167
190
  *
168
- * stdio → { command, args, env } (portable field names)
169
- * remote { url, headers? }
191
+ * stdio → { command, args, env } (portable field names; `type` omitted —
192
+ * OMP defaults an entry with no `type` to stdio)
193
+ * remote → { type: "http"|"sse", url, headers? } (`type` is REQUIRED for a
194
+ * URL server — without it OMP would mis-parse the entry as stdio)
170
195
  *
171
196
  * Telemetry wrapping routes the real command through
172
197
  * `<homeBin> serve --connector <id> -- <command> <args...>`. OMP has no native
@@ -199,6 +224,7 @@ var OMPAdapter = class extends BaseAdapter {
199
224
  return entry2;
200
225
  }
201
226
  const entry = {
227
+ type: server.transport === "sse" ? "sse" : "http",
202
228
  url: resolveEnvRefsDeep(server.url ?? "")
203
229
  };
204
230
  const headers = this.renderEnv(server.headers);
@@ -213,7 +239,10 @@ var OMPAdapter = class extends BaseAdapter {
213
239
  // ── Hook install / uninstall (ts-plugin extension package) ──────────────
214
240
  installHooks(ctx) {
215
241
  const entryPath = this.getHookConfigPath(ctx);
216
- if (ctx.connector.platforms[HOST]?.hooks === false) {
242
+ const nativeEvents = Object.keys(
243
+ ctx.connector.platforms[HOST]?.nativeHooks ?? {}
244
+ );
245
+ if (ctx.connector.platforms[HOST]?.hooks === false && nativeEvents.length === 0) {
217
246
  return [
218
247
  {
219
248
  platform: this.id,
@@ -223,16 +252,70 @@ var OMPAdapter = class extends BaseAdapter {
223
252
  }
224
253
  ];
225
254
  }
226
- if (ctx.connector.hookEvents.length === 0) {
255
+ if (ctx.connector.hookEvents.length === 0 && nativeEvents.length === 0) {
227
256
  return [
228
257
  {
229
258
  platform: this.id,
230
259
  action: "skip",
231
260
  path: entryPath,
232
- detail: "connector declares no hooks"
261
+ detail: this.actionTriggers(ctx).length > 0 ? "no hooks (plugin written for actions)" : "connector declares no hooks"
233
262
  }
234
263
  ];
235
264
  }
265
+ return this.ensurePlugin(
266
+ ctx,
267
+ (file) => file.path.endsWith("package.json") ? "omp extension manifest" : `omp plugin module (${this.hookDetail(ctx)})`
268
+ );
269
+ }
270
+ // ── Action install / uninstall (slash commands in the shared plugin) ────
271
+ /**
272
+ * Each declared action becomes a `pi.registerCommand(...)` inside the SAME
273
+ * generated extension module (see buildPluginSource). So installActions just
274
+ * ensures that shared module is on disk: when hooks already wrote the identical
275
+ * bytes the content-compare yields "skip" (no double material write), and an
276
+ * actions-only connector gets the plugin written here. Honors
277
+ * platforms.omp.actions === false and the empty-actions skip.
278
+ */
279
+ installActions(ctx) {
280
+ const entryPath = this.getHookConfigPath(ctx);
281
+ if (ctx.connector.platforms[HOST]?.actions === false) {
282
+ return [
283
+ { platform: this.id, action: "skip", path: entryPath, detail: "actions disabled for omp" }
284
+ ];
285
+ }
286
+ if (this.actionTriggers(ctx).length === 0) {
287
+ return [
288
+ { platform: this.id, action: "skip", path: entryPath, detail: "connector declares no actions" }
289
+ ];
290
+ }
291
+ return this.ensurePlugin(
292
+ ctx,
293
+ (file) => file.path.endsWith("package.json") ? "omp extension manifest" : "omp plugin module (action commands)"
294
+ );
295
+ }
296
+ /**
297
+ * Actions live INSIDE the shared plugin module, which uninstallHooks removes
298
+ * unconditionally (it runs last in the teardown pass). Removing it here too
299
+ * would race uninstallHooks, so this is a single informational skip.
300
+ */
301
+ uninstallActions(ctx) {
302
+ return [
303
+ {
304
+ platform: this.id,
305
+ action: "skip",
306
+ path: this.getHookConfigPath(ctx),
307
+ detail: "action commands are embedded in the plugin module; removed by uninstallHooks"
308
+ }
309
+ ];
310
+ }
311
+ /**
312
+ * Synthesize + content-compare-write the extension package (package.json +
313
+ * index.js). Shared by installHooks and installActions: the generator is a pure
314
+ * function of ctx, so whichever surface runs second sees byte-identical files
315
+ * and reports "skip" — no double material write. `detailFor` flavors the
316
+ * ChangeRecord detail per surface.
317
+ */
318
+ ensurePlugin(ctx, detailFor) {
236
319
  const files = this.synthesizePlugin(ctx);
237
320
  const changes = [];
238
321
  for (const file of files) {
@@ -250,7 +333,7 @@ var OMPAdapter = class extends BaseAdapter {
250
333
  platform: this.id,
251
334
  action,
252
335
  path: file.path,
253
- detail: file.path.endsWith("package.json") ? "omp extension manifest" : `omp plugin module (${this.hookDetail(ctx)})`
336
+ detail: detailFor(file)
254
337
  });
255
338
  }
256
339
  return changes;
@@ -262,10 +345,18 @@ var OMPAdapter = class extends BaseAdapter {
262
345
  * "unsupported here" so the detail never overstates coverage.
263
346
  */
264
347
  hookDetail(ctx) {
265
- const declared = ctx.connector.hookEvents;
348
+ const canonicalOff = ctx.connector.platforms[HOST]?.hooks === false;
349
+ const declared = canonicalOff ? [] : ctx.connector.hookEvents;
266
350
  const mapped = declared.filter((e) => EVENT_TO_OMP[e] !== void 0);
267
351
  const unsupported = declared.filter((e) => EVENT_TO_OMP[e] === void 0);
268
- const base = mapped.join(",");
352
+ const nativeEvents = Object.keys(
353
+ ctx.connector.platforms[HOST]?.nativeHooks ?? {}
354
+ );
355
+ const parts = [mapped.join(",")].filter((p) => p !== "");
356
+ if (nativeEvents.length > 0) {
357
+ parts.push(`native: ${nativeEvents.join(",")}`);
358
+ }
359
+ const base = parts.join("; ");
269
360
  return unsupported.length > 0 ? `${base}; unsupported here: ${unsupported.join(",")}` : base;
270
361
  }
271
362
  uninstallHooks(ctx) {
@@ -342,7 +433,8 @@ var OMPAdapter = class extends BaseAdapter {
342
433
  buildPluginSource(ctx) {
343
434
  const homeBin = JSON.stringify(ctx.homeBinPath);
344
435
  const connectorId = JSON.stringify(ctx.connector.id);
345
- const events = ctx.connector.hookEvents.filter(
436
+ const canonicalOff = ctx.connector.platforms[HOST]?.hooks === false;
437
+ const events = canonicalOff ? [] : ctx.connector.hookEvents.filter(
346
438
  (e) => EVENT_TO_OMP[e] !== void 0
347
439
  );
348
440
  const has = (e) => events.includes(e);
@@ -461,11 +553,39 @@ function deriveSessionId(ctx) {
461
553
  pi.on("session_before_compact", () => {
462
554
  bridge("PreCompact", { sessionId: SESSION_ID, projectDir: PROJECT_DIR });
463
555
  return undefined;
556
+ });`);
557
+ }
558
+ const nativeEvents = Object.keys(
559
+ ctx.connector.platforms[HOST]?.nativeHooks ?? {}
560
+ );
561
+ for (const ev of nativeEvents) {
562
+ handlers.push(` // nativeHooks passthrough \u2192 ${ev}
563
+ pi.on(${JSON.stringify(ev)}, (event) => {
564
+ bridge(${JSON.stringify(ev)}, { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });
565
+ return undefined;
566
+ });`);
567
+ }
568
+ const actionHandlers = [];
569
+ for (const action of this.actionTriggers(ctx)) {
570
+ const id = JSON.stringify(action.id);
571
+ const description = JSON.stringify(action.description);
572
+ actionHandlers.push(` // Action /${action.id} \u2192 run the home-bin action verb directly.
573
+ pi.registerCommand(${id}, {
574
+ description: ${description},
575
+ handler: async (_args, _ctx) => {
576
+ try {
577
+ const args = ["action", "omp", ${id}, "--connector", CONNECTOR_ID];
578
+ process.platform === "win32"
579
+ ? execSync([HOME_BIN, ...args].map((a) => '"' + a + '"').join(" "), { stdio: "inherit" })
580
+ : execFileSync(HOME_BIN, args, { stdio: "inherit" });
581
+ } catch { /* fail-open */ }
582
+ return undefined;
583
+ },
464
584
  });`);
465
585
  }
466
586
  const factory = `
467
587
  export default function plugin(pi) {
468
- ${handlers.join("\n\n")}
588
+ ${[...handlers, ...actionHandlers].join("\n\n")}
469
589
  }
470
590
  `;
471
591
  return header + factory;
@@ -535,7 +655,7 @@ ${handlers.join("\n\n")}
535
655
  const entryPath = this.getHookConfigPath(ctx);
536
656
  const manifestPath = join(this.extensionDir(ctx), "package.json");
537
657
  const connectorId = ctx.connector.id;
538
- const hasHooks = ctx.connector.hookEvents.length > 0;
658
+ const hasHooks = ctx.connector.hookEvents.length > 0 || this.actionTriggers(ctx).length > 0;
539
659
  return [
540
660
  {
541
661
  name: `${this.name}: mcp.json present`,
@@ -594,4 +714,4 @@ export {
594
714
  adapter,
595
715
  omp_default as default
596
716
  };
597
- //# sourceMappingURL=omp-Z6FRNDPN.js.map
717
+ //# sourceMappingURL=omp-2QMEY6HD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/omp/index.ts"],"sourcesContent":["/**\n * adapters/omp — Oh My Pi (OMP) platform adapter for agent-connector.\n *\n * OMP is a ts-plugin host with NATIVE MCP. The two surfaces are split:\n *\n * • MCP (native, real file) — OMP reads a real `mcp.json`:\n * user scope → <agentDir>/mcp.json (agentDir = ~/.omp/agent)\n * project scope → <projectDir>/.omp/mcp.json\n * JSON, root key \"mcpServers\", stdio entry { command, args, env } — the\n * portable field names. Verified against can1357/oh-my-pi\n * (packages/utils/src/dirs.ts getMCPConfigPath + docs/mcp-config.md). OMP\n * documents no native ${env:VAR} token, so env refs resolve to literals at\n * install time (resolveEnvRefsDeep).\n *\n * • Hooks (ts-plugin, in-process) — OMP loads EXTENSION PACKAGES. Its plugin\n * loader reads a package whose package.json carries an `omp` (or `pi`)\n * manifest field; the manifest's `extensions` array points at the plugin\n * module, whose DEFAULT export is a HookFactory `(pi) => void` that calls\n * `pi.on(event, handler)` (upstream loader.ts:75\n * `pluginPkg.omp || pluginPkg.pi`; hook factory types.ts:809). context-mode\n * proved this by dropping an extension under\n * `<agentDir>/extensions/<name>/{package.json,index.js}` whose index.js\n * default-exports the OMP plugin factory (src/setup.ts applyPlatformInstall\n * \"omp\"; src/adapters/omp/plugin.ts).\n *\n * Why we SYNTHESIZE instead of importing context-mode's plugin:\n * context-mode could load its own plugin module in-process because the handler\n * code shipped with the package. agent-connector cannot — the connector's hook\n * handlers are arbitrary developer code we must not import into OMP's runtime\n * (wrong cwd, wrong deps, version skew, the cache-heal bug class). So, exactly\n * like the OpenCode adapter, we generate a tiny self-contained ESM extension\n * that imports NOTHING from agent-connector: each `pi.on(...)` handler shells\n * out to the ONE stable home binary's universal entrypoint\n * (`<homeBin> hook omp <event> --connector <id>`) over child_process, feeds it\n * the OMP-shaped payload as JSON on stdin, and JSON.parses the normalized\n * HookResponse back from stdout. Fail-open: any bridge error → no-op. One\n * entrypoint, every paradigm.\n *\n * Event mapping (only events the connector declares are subscribed):\n * PreToolUse → pi.on(\"tool_call\", …) block via { block:true, reason }\n * (OMP tool_call cannot mutate args → canModifyArgs:false)\n * PostToolUse → pi.on(\"tool_result\", …) observe-only (no output rewrite hook)\n * SessionStart → pi.on(\"session_start\", …) (no context-injection surface)\n * PreCompact → pi.on(\"session_before_compact\", …) observe-only\n *\n * Distinct storage root from Pi: OMP lives under ~/.omp (NOT ~/.pi); the\n * PI_CODING_AGENT_DIR env var (the only OMP runtime dir override — OMP mirrors\n * any .env `OMP_*` keys to `PI_*` before process.env is read) takes precedence.\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 } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type {\n Adapter,\n GeneratedPluginFile,\n HookReply,\n InstallContext,\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 PreCompactEvent,\n PreToolUseEvent,\n ServerDef,\n SessionStartEvent,\n Transport,\n} from \"../../core/types.js\";\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport {\n buildServeWrapperCommand,\n shouldWrapForTelemetry,\n} from \"../../core/spawn.js\";\n\nconst HOST: PlatformId = \"omp\";\nconst MCP_ROOT_KEY = \"mcpServers\";\n\n/**\n * Canonical → OMP event name map. A connector hook event is only subscribed by\n * the generated plugin when it appears here AND is declared by the connector.\n * Names verified against the proven context-mode OMP plugin (pi.on targets).\n */\nconst EVENT_TO_OMP: Partial<Record<HookEventName, string>> = {\n PreToolUse: \"tool_call\",\n PostToolUse: \"tool_result\",\n SessionStart: \"session_start\",\n PreCompact: \"session_before_compact\",\n};\n\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\ninterface OmpBridgePayload {\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolOutput?: string;\n isError?: boolean;\n sessionId?: string;\n projectDir?: string;\n}\n\n/** Native MCP server entry shapes OMP accepts under \"mcpServers\". */\ninterface OmpStdioServer {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n}\ninterface OmpRemoteServer {\n type: \"http\" | \"sse\";\n url: string;\n headers?: Record<string, string>;\n}\n\nexport class OMPAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name = \"Oh My Pi (OMP)\";\n readonly paradigm: HookParadigm = \"ts-plugin\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\n supportsMemory: true,\n preToolUse: true,\n postToolUse: true,\n // session_before_compact is wired as an observe-only PreCompact surface.\n preCompact: true,\n sessionStart: true,\n sessionEnd: false,\n userPromptSubmit: false,\n stop: false,\n notification: false,\n // Newer events: OMP's agent_start/agent_end/turn_* hooks are the MAIN loop\n // (no subagent lifecycle events), there is no permission/approval surface,\n // and tool failure is merged into tool_result (isError) rather than a\n // dedicated event. None are wired, so permissionRequest /\n // postToolUseFailure / subagentStart / subagentStop stay unset — the\n // generated bridge never references them and install reports them as\n // \"unsupported here\".\n // OMP tool_call gates via { block, reason } only — it does not hand the\n // handler a mutable args object, so input rewrite is unsupported.\n canModifyArgs: false,\n // No tool_result output-rewrite surface upstream.\n canModifyOutput: false,\n // No session_start context-injection surface upstream.\n canInjectSessionContext: false,\n // OMP registers stdio and remote (streamable http + sse) MCP servers.\n transports: [\"stdio\", \"sse\", \"http\"],\n // Action surface: each declared action becomes a user-invokable slash\n // command registered INSIDE the generated plugin module (pi.registerCommand),\n // shelling out to the home-bin `action` verb. supportsActions → the\n // installActions/uninstallActions overrides below.\n supportsActions: true,\n // Native passthrough hooks: OMP's main-loop lifecycle events —\n // agent_start, agent_end, turn_start, turn_end — have NO canonical\n // HookEventName analog (verified in oh-my-pi shared-events.ts). A connector\n // reaches them by declaring platforms[\"omp\"].nativeHooks; the generated\n // plugin registers + bridges each declared native event verbatim\n // (buildPluginSource native loop), and the home-bin runtime dispatches it\n // host-generically via runNativeHook.\n supportsNativeHooks: true,\n };\n\n // ── Native paths ───────────────────────────────────────────────────────\n\n /**\n * Resolve the OMP agent dir, honoring the PI_CODING_AGENT_DIR override (the\n * only OMP runtime dir env), else ~/.omp/agent. This is the user-scope root.\n */\n private agentDir(): string {\n const override = process.env.PI_CODING_AGENT_DIR;\n return override && override !== \"\"\n ? override\n : join(homedir(), \".omp\", \"agent\");\n }\n\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".omp\")\n : this.agentDir();\n }\n\n /** Native MCP file: <agentDir>/mcp.json (user) or <projectDir>/.omp/mcp.json. */\n getServerConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"mcp.json\");\n }\n\n /**\n * For this ts-plugin host the \"hook config path\" is the generated extension\n * entrypoint module (index.js). OMP loads the extension package by reading its\n * package.json manifest, so the package.json is written alongside (see\n * synthesizePlugin); this returns the entry module the manifest points at.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return join(this.extensionDir(ctx), \"index.js\");\n }\n\n /** Extension package directory OMP loads the plugin from, per scope. */\n private extensionDir(ctx: InstallContext): string {\n const base =\n ctx.scope === \"project\"\n ? join(ctx.projectDir, \".omp\")\n : this.agentDir();\n return join(base, \"extensions\", ctx.connector.id);\n }\n\n // ── Detection ──────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userAgentDir = this.agentDir();\n const userConfig = join(userAgentDir, \"mcp.json\");\n const projectConfig = join(projectDir, \".omp\", \"mcp.json\");\n\n const userInstalled = existsSync(userAgentDir) || existsSync(userConfig);\n const projInstalled =\n existsSync(join(projectDir, \".omp\")) || existsSync(projectConfig);\n const installed = userInstalled || projInstalled;\n\n // Report the marker that actually matched.\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 OMP dir at ${join(projectDir, \".omp\")}`\n : `found OMP agent dir at ${userAgentDir}`\n : `no OMP agent dir at ${userAgentDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── MCP server install / uninstall (native mcp.json) ────────────────────\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 omp\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n const changes: ChangeRecord[] = [];\n // Honesty: OMP documents stdio/sse/http. A connector declaring another\n // transport (e.g. the non-spec \"ws\") is still rendered best-effort — the\n // remote branch coerces it to type:\"http\" (OMP rejects an unknown `type`) —\n // but the mismatch is REPORTED, never silently downgraded (same \"degradation\n // is never silent\" posture as the hook warn-skips).\n if (!this.capabilities.transports.includes(server.transport)) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n path: serverPath,\n detail: `transport \"${server.transport}\" is not an OMP MCP transport (stdio/sse/http); registered best-effort as type:\"http\"`,\n });\n }\n\n const entry = this.renderServerEntry(ctx, server);\n changes.push(\n this.upsertServerInJson(\n serverPath,\n MCP_ROOT_KEY,\n ctx.connector.id,\n entry,\n ctx.dryRun,\n ),\n );\n return changes;\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [\n this.removeServerFromJson(\n serverPath,\n MCP_ROOT_KEY,\n ctx.connector.id,\n ctx.dryRun,\n ),\n ];\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[HOST]?.server;\n if (override === false) return undefined;\n const base = ctx.connector.server;\n if (!base) return undefined;\n return override && typeof override === \"object\"\n ? { ...base, ...override }\n : base;\n }\n\n /**\n * Render a normalized ServerDef into OMP's native \"mcpServers\" entry.\n *\n * stdio → { command, args, env } (portable field names; `type` omitted —\n * OMP defaults an entry with no `type` to stdio)\n * remote → { type: \"http\"|\"sse\", url, headers? } (`type` is REQUIRED for a\n * URL server — without it OMP would mis-parse the entry as stdio)\n *\n * Telemetry wrapping routes the real command through\n * `<homeBin> serve --connector <id> -- <command> <args...>`. OMP has no native\n * env interpolation token, so every ${env:VAR} ref resolves to a literal.\n */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): OmpStdioServer | OmpRemoteServer {\n const transport: Transport = server.transport;\n\n if (transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\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 const entry: OmpStdioServer = {\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 return entry;\n }\n\n // remote (http / sse) — OMP registers a URL with an explicit `type`\n // discriminator. OMP defaults a type-less entry to stdio (docs/mcp-config.md\n // \"stdio is the default when type is omitted\"), so a URL server MUST carry\n // its type or it is mis-parsed as a command.\n const entry: OmpRemoteServer = {\n type: server.transport === \"sse\" ? \"sse\" : \"http\",\n url: resolveEnvRefsDeep(server.url ?? \"\"),\n };\n const headers = this.renderEnv(server.headers);\n if (headers) entry.headers = headers;\n return entry;\n }\n\n /** Resolve every ${env:VAR} ref to a literal (OMP has 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 // ── Hook install / uninstall (ts-plugin extension package) ──────────────\n\n installHooks(ctx: InstallContext): ChangeRecord[] {\n const entryPath = this.getHookConfigPath(ctx);\n\n // Native passthrough events are a sibling, omp-scoped declaration: `hooks:\n // false` disables only the CANONICAL events, and the generated native loop\n // reads platforms.omp.nativeHooks directly (independent of hookEvents). So\n // whenever native events exist the plugin must still be synthesized.\n const nativeEvents = Object.keys(\n ctx.connector.platforms[HOST]?.nativeHooks ?? {},\n );\n\n if (ctx.connector.platforms[HOST]?.hooks === false && nativeEvents.length === 0) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: entryPath,\n detail: \"hooks disabled for omp\",\n },\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 — but that is\n // installActions' job. installHooks itself only writes when there ARE hooks,\n // so its skip detail stays honest about why (no hooks) while admitting the\n // plugin may still exist for actions. Native events are a third reason to\n // write it, so they suppress the skip too.\n if (ctx.connector.hookEvents.length === 0 && nativeEvents.length === 0) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: entryPath,\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, (file) =>\n file.path.endsWith(\"package.json\")\n ? \"omp extension manifest\"\n : `omp plugin module (${this.hookDetail(ctx)})`,\n );\n }\n\n // ── Action install / uninstall (slash commands in the shared plugin) ────\n\n /**\n * Each declared action becomes a `pi.registerCommand(...)` inside the SAME\n * generated extension module (see buildPluginSource). So installActions just\n * ensures that shared module is on disk: when hooks already wrote the identical\n * bytes the content-compare yields \"skip\" (no double material write), and an\n * actions-only connector gets the plugin written here. Honors\n * platforms.omp.actions === false and the empty-actions skip.\n */\n override installActions(ctx: InstallContext): ChangeRecord[] {\n const entryPath = this.getHookConfigPath(ctx);\n if (ctx.connector.platforms[HOST]?.actions === false) {\n return [\n { platform: this.id, action: \"skip\", path: entryPath, detail: \"actions disabled for omp\" },\n ];\n }\n if (this.actionTriggers(ctx).length === 0) {\n return [\n { platform: this.id, action: \"skip\", path: entryPath, detail: \"connector declares no actions\" },\n ];\n }\n return this.ensurePlugin(ctx, (file) =>\n file.path.endsWith(\"package.json\")\n ? \"omp extension manifest\"\n : \"omp plugin module (action commands)\",\n );\n }\n\n /**\n * Actions live INSIDE the shared plugin module, which uninstallHooks removes\n * unconditionally (it runs last in the teardown pass). Removing it here too\n * would race uninstallHooks, 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 extension package (package.json +\n * index.js). Shared by installHooks and installActions: the generator is a pure\n * function of ctx, so whichever surface runs second sees byte-identical files\n * and reports \"skip\" — no double material write. `detailFor` flavors the\n * ChangeRecord detail per surface.\n */\n private ensurePlugin(\n ctx: InstallContext,\n detailFor: (file: GeneratedPluginFile) => string,\n ): ChangeRecord[] {\n const files = this.synthesizePlugin(ctx);\n const changes: ChangeRecord[] = [];\n\n for (const file of files) {\n const before = existsSync(file.path)\n ? this.safeRead(file.path)\n : 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\n changes.push({\n platform: this.id,\n action,\n path: file.path,\n detail: detailFor(file),\n });\n }\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_OMP are mapped/wired; any declared\n * event with no OMP mapping (e.g. UserPromptSubmit) is reported separately as\n * \"unsupported here\" so the detail never overstates coverage.\n */\n private hookDetail(ctx: InstallContext): string {\n // `hooks: false` suppresses the canonical handlers (buildPluginSource), so\n // the detail must not claim them. Native events are still reported below.\n const canonicalOff = ctx.connector.platforms[HOST]?.hooks === false;\n const declared = canonicalOff ? [] : ctx.connector.hookEvents;\n const mapped = declared.filter((e) => EVENT_TO_OMP[e] !== undefined);\n const unsupported = declared.filter((e) => EVENT_TO_OMP[e] === undefined);\n const nativeEvents = Object.keys(\n ctx.connector.platforms[HOST]?.nativeHooks ?? {},\n );\n const parts = [mapped.join(\",\")].filter((p) => p !== \"\");\n if (nativeEvents.length > 0) {\n parts.push(`native: ${nativeEvents.join(\",\")}`);\n }\n const base = parts.join(\"; \");\n return unsupported.length > 0\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\n : base;\n }\n\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\n const entryPath = this.getHookConfigPath(ctx);\n const manifestPath = join(this.extensionDir(ctx), \"package.json\");\n const present = [entryPath, manifestPath].filter((p) => existsSync(p));\n\n if (present.length === 0) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: entryPath,\n detail: \"no omp extension present\",\n },\n ];\n }\n\n const changes: ChangeRecord[] = [];\n for (const p of present) {\n if (!ctx.dryRun) rmSync(p, { force: true });\n changes.push({\n platform: this.id,\n action: \"remove\",\n path: p,\n detail: p.endsWith(\"package.json\")\n ? \"omp extension manifest\"\n : \"omp plugin module\",\n });\n }\n return changes;\n }\n\n // ── ts-plugin synthesis ────────────────────────────────────────────────\n\n /**\n * Build the OMP extension package: TWO files.\n *\n * 1. package.json — the manifest OMP's loader reads. It carries the `omp`\n * field (`pluginPkg.omp || pluginPkg.pi`) whose `extensions` array points\n * at the entry module, plus `type:\"module\"` and `main:\"index.js\"`.\n * 2. index.js — a self-contained ESM plugin that imports nothing from\n * agent-connector. Its default export is the OMP HookFactory `(pi)=>void`;\n * each declared event registers a `pi.on(...)` handler that bridges to the\n * home binary via execFileSync (fail-open) and applies the normalized\n * response.\n */\n synthesizePlugin(ctx: InstallContext): GeneratedPluginFile[] {\n const dir = this.extensionDir(ctx);\n return [\n {\n path: join(dir, \"package.json\"),\n contents: this.buildManifest(ctx),\n executable: false,\n },\n {\n path: join(dir, \"index.js\"),\n contents: this.buildPluginSource(ctx),\n executable: false,\n },\n ];\n }\n\n /** The extension package.json manifest OMP's plugin loader reads. */\n private buildManifest(ctx: InstallContext): string {\n const manifest = {\n name: ctx.connector.id,\n version: ctx.connector.version || \"0.0.0\",\n description: `${ctx.connector.displayName} OMP extension (generated by agent-connector)`,\n type: \"module\",\n main: \"index.js\",\n // pluginPkg.omp || pluginPkg.pi → manifest. extensions points at the entry.\n omp: {\n extensions: [\"./index.js\"],\n },\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\n // `platforms.omp.hooks === false` disables only the CANONICAL events (the\n // native loop below reads platforms.omp.nativeHooks directly and is a\n // sibling, unaffected). When canonical hooks are off the module may still be\n // synthesized for native events and/or actions, so the canonical handler set\n // collapses to empty here rather than short-circuiting the whole plugin.\n const canonicalOff = ctx.connector.platforms[HOST]?.hooks === false;\n const events = canonicalOff\n ? []\n : ctx.connector.hookEvents.filter(\n (e): e is HookEventName => EVENT_TO_OMP[e] !== undefined,\n );\n const has = (e: HookEventName) => events.includes(e);\n\n const header = `/**\n * AUTO-GENERATED by agent-connector — DO NOT EDIT.\n *\n * Self-contained Oh My Pi (OMP) plugin extension for connector ${ctx.connector.id}.\n * It imports nothing from agent-connector: every hook invocation shells out to\n * the stable home binary's universal entrypoint and JSON-parses the normalized\n * response. Fail-open: any bridge error degrades to a no-op (never wedges OMP).\n *\n * OMP loads this module via the sibling package.json \"omp\" manifest field; the\n * default export is the HookFactory (pi) => void called once at load time.\n */\nimport { execFileSync, execSync } from \"node:child_process\";\n\nconst HOME_BIN = ${homeBin};\nconst CONNECTOR_ID = ${connectorId};\n\n/**\n * Invoke the universal hook entrypoint for one event.\n * @param {string} event canonical event name\n * @param {object} payload OMP-shaped payload posted on stdin\n * @returns {object|null} normalized HookResponse, or null on any failure\n */\nfunction bridge(event, payload) {\n try {\n // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\n // execFile a batch file, and shell+args is deprecated (DEP0190), so run one\n // quoted command line via a shell. POSIX keeps the direct execFile (no shell).\n const args = [\"hook\", \"omp\", event, \"--connector\", CONNECTOR_ID];\n const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\n const stdout =\n process.platform === \"win32\"\n ? execSync([HOME_BIN, ...args].map((a) => '\"' + a + '\"').join(\" \"), opts)\n : execFileSync(HOME_BIN, args, opts);\n const text = (stdout || \"\").trim();\n if (text === \"\") return { decision: \"allow\" };\n return JSON.parse(text);\n } catch {\n // Fail-open — never break an OMP tool call / lifecycle event.\n return null;\n }\n}\n\n// OMP exposes the project dir via PI_PROJECT_DIR (PI_*-prefixed only); fall\n// through to cwd. The plugin process is long-lived, so resolve once at load.\nconst PROJECT_DIR = process.env.PI_PROJECT_DIR || process.cwd();\n\n// Derive a stable session id from OMP's session manager when available\n// (ctx.sessionManager.getSessionFile()), else a wall-clock token. The id is\n// rebound on each session_start so multi-session reuse stays attributed.\nlet SESSION_ID = \"\";\nfunction deriveSessionId(ctx) {\n try {\n const f = ctx && ctx.sessionManager && typeof ctx.sessionManager.getSessionFile === \"function\"\n ? ctx.sessionManager.getSessionFile()\n : \"\";\n if (f && typeof f === \"string\") return f;\n } catch {\n // best effort\n }\n return \"omp-\" + Date.now();\n}\n`;\n\n const handlers: string[] = [];\n\n if (has(\"SessionStart\")) {\n handlers.push(` // SessionStart → rebind the session id and notify the connector.\n pi.on(\"session_start\", (_event, ctx) => {\n SESSION_ID = deriveSessionId(ctx);\n bridge(\"SessionStart\", { sessionId: SESSION_ID, projectDir: PROJECT_DIR });\n return undefined;\n });`);\n }\n\n if (has(\"PreToolUse\")) {\n handlers.push(` // PreToolUse → block via { block, reason }. OMP tool_call cannot mutate\n // args, so \"ask\" degrades to a block (the safe direction); \"modify\" degrades\n // to ALLOW (no-block), matching opencode/claude-code — only deny/ask block.\n pi.on(\"tool_call\", (event) => {\n const payload = {\n toolName: (event && event.toolName) || \"\",\n toolInput: (event && event.input) || {},\n sessionId: SESSION_ID,\n projectDir: PROJECT_DIR,\n };\n const res = bridge(\"PreToolUse\", payload);\n if (!res) return undefined;\n if (res.decision === \"deny\" || res.decision === \"ask\") {\n return { block: true, reason: res.reason || \"Blocked by ${ctx.connector.id}\" };\n }\n return undefined;\n });`);\n }\n\n if (has(\"PostToolUse\")) {\n handlers.push(` // PostToolUse → observe tool results (no output-rewrite surface upstream).\n pi.on(\"tool_result\", (event) => {\n const content = (event && Array.isArray(event.content)) ? event.content : [];\n const toolOutput = content\n .filter((c) => c && c.type === \"text\" && typeof c.text === \"string\")\n .map((c) => c.text)\n .join(\"\\\\n\");\n const payload = {\n toolName: (event && event.toolName) || \"\",\n toolInput: (event && event.input) || {},\n toolOutput,\n isError: !!(event && event.isError),\n sessionId: SESSION_ID,\n projectDir: PROJECT_DIR,\n };\n bridge(\"PostToolUse\", payload);\n return undefined;\n });`);\n }\n\n if (has(\"PreCompact\")) {\n handlers.push(` // PreCompact → notify before OMP compacts the context window.\n pi.on(\"session_before_compact\", () => {\n bridge(\"PreCompact\", { sessionId: SESSION_ID, projectDir: PROJECT_DIR });\n return undefined;\n });`);\n }\n\n // NATIVE passthrough events: OMP's main-loop lifecycle hooks (agent_start,\n // agent_end, turn_start, turn_end) have NO canonical analog, so they are not\n // in EVENT_TO_OMP. Each declared native event registers a pi.on(...) handler\n // that bridges the NATIVE event name verbatim to the home-bin → runNativeHook\n // dispatches it host-generically. Read straight off platforms.omp.nativeHooks\n // (independent of hookEvents), so they install even with hooks:false.\n const nativeEvents = Object.keys(\n ctx.connector.platforms[HOST]?.nativeHooks ?? {},\n );\n for (const ev of nativeEvents) {\n handlers.push(` // nativeHooks passthrough → ${ev}\n pi.on(${JSON.stringify(ev)}, (event) => {\n bridge(${JSON.stringify(ev)}, { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });\n return undefined;\n });`);\n }\n\n // Action surface: one pi.registerCommand per declared action. The command\n // shells out to the home-bin `action` verb (host token literal \"omp\" — OMP\n // has no fork). Fail-open: a launcher error degrades to a no-op (never wedges\n // OMP). Both id AND description are JSON.stringify'd — descriptions are\n // free-form connector text, so a raw \" / backtick would break the module.\n const actionHandlers: string[] = [];\n for (const action of this.actionTriggers(ctx)) {\n const id = JSON.stringify(action.id);\n const description = JSON.stringify(action.description);\n actionHandlers.push(` // Action /${action.id} → run the home-bin action verb directly.\n pi.registerCommand(${id}, {\n description: ${description},\n handler: async (_args, _ctx) => {\n try {\n const args = [\"action\", \"omp\", ${id}, \"--connector\", CONNECTOR_ID];\n process.platform === \"win32\"\n ? execSync([HOME_BIN, ...args].map((a) => '\"' + a + '\"').join(\" \"), { stdio: \"inherit\" })\n : execFileSync(HOME_BIN, args, { stdio: \"inherit\" });\n } catch { /* fail-open */ }\n return undefined;\n },\n });`);\n }\n\n // When hook events are empty but actions exist, the factory body is just the\n // action registrations (still valid JS — an empty handlers[] yields \"\").\n const factory = `\nexport default function plugin(pi) {\n${[...handlers, ...actionHandlers].join(\"\\n\\n\")}\n}\n`;\n\n return header + factory;\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?, projectDir? }\n * so this maps straight through.\n */\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\n const input = (raw ?? {}) as OmpBridgePayload;\n const base = {\n hostPlatform: HOST,\n connectorId: \"\",\n sessionId: typeof input.sessionId === \"string\" ? input.sessionId : \"\",\n raw,\n ...(typeof input.projectDir === \"string\"\n ? { projectDir: input.projectDir }\n : {}),\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\"\n ? { toolOutput: input.toolOutput }\n : {}),\n ...(typeof input.isError === \"boolean\"\n ? { isError: input.isError }\n : {}),\n };\n return ev;\n }\n case \"PreCompact\": {\n const ev: PreCompactEvent = { ...base };\n return ev;\n }\n case \"SessionStart\": {\n const ev: SessionStartEvent = { ...base, source: \"startup\" };\n return ev;\n }\n default:\n // Other canonical events are not surfaced by OMP; 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 maps decision\n * → OMP's { block, reason } for tool_call (and ignores it otherwise).\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 serverPath = this.getServerConfigPath(ctx);\n const entryPath = this.getHookConfigPath(ctx);\n const manifestPath = join(this.extensionDir(ctx), \"package.json\");\n const connectorId = ctx.connector.id;\n // Actions are health-equivalent to hooks: both write the SAME extension\n // package, so an actions-only connector must still assert it is present.\n const hasHooks =\n ctx.connector.hookEvents.length > 0 || this.actionTriggers(ctx).length > 0;\n\n return [\n {\n name: `${this.name}: mcp.json present`,\n check: () =>\n existsSync(serverPath)\n ? { status: \"OK\", detail: serverPath }\n : { status: \"FAIL\", detail: `not found: ${serverPath}` },\n },\n {\n name: `${this.name}: ${MCP_ROOT_KEY}.${connectorId} registered`,\n check: () => {\n if (!ctx.connector.server) {\n return { status: \"OK\", detail: \"no MCP server declared\" };\n }\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(\n serverPath,\n );\n const bucket = cfg?.[MCP_ROOT_KEY];\n if (!cfg || !bucket) {\n return {\n status: \"FAIL\",\n detail: `no ${MCP_ROOT_KEY} in ${serverPath}`,\n };\n }\n return connectorId in bucket\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\n : {\n status: \"FAIL\",\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${serverPath}`,\n };\n },\n },\n {\n name: `${this.name}: extension package present`,\n check: () => {\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\n if (!existsSync(manifestPath)) {\n return { status: \"FAIL\", detail: `not found: ${manifestPath}` };\n }\n return existsSync(entryPath)\n ? { status: \"OK\", detail: entryPath }\n : { status: \"FAIL\", detail: `not found: ${entryPath}` };\n },\n },\n ];\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/** 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\nexport const adapter = new OMPAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
@@ -3,11 +3,11 @@ import {
3
3
  OpenClawAdapter,
4
4
  adapter,
5
5
  openclaw_default
6
- } from "./chunk-KGGBNKI4.js";
6
+ } from "./chunk-URHQMFB3.js";
7
7
  import "./chunk-DBQMX75U.js";
8
- import "./chunk-IW77FFNX.js";
8
+ import "./chunk-CPLK7GTE.js";
9
9
  import "./chunk-7L6ITC4T.js";
10
- import "./chunk-BXGFUENQ.js";
10
+ import "./chunk-I5MLE6S7.js";
11
11
  import "./chunk-N6HH7K6P.js";
12
12
  import "./chunk-TRQKHP7D.js";
13
13
  export {
@@ -15,4 +15,4 @@ export {
15
15
  adapter,
16
16
  openclaw_default as default
17
17
  };
18
- //# sourceMappingURL=openclaw-7G437NT7.js.map
18
+ //# sourceMappingURL=openclaw-EXJEFNZI.js.map
@@ -4,12 +4,12 @@ import {
4
4
  } from "./chunk-DBQMX75U.js";
5
5
  import {
6
6
  BaseAdapter
7
- } from "./chunk-IW77FFNX.js";
7
+ } from "./chunk-CPLK7GTE.js";
8
8
  import "./chunk-7L6ITC4T.js";
9
9
  import {
10
10
  buildServeWrapperCommand,
11
11
  shouldWrapForTelemetry
12
- } from "./chunk-BXGFUENQ.js";
12
+ } from "./chunk-I5MLE6S7.js";
13
13
  import "./chunk-N6HH7K6P.js";
14
14
  import "./chunk-TRQKHP7D.js";
15
15
 
@@ -32,7 +32,12 @@ var EVENT_TO_OPENCODE = {
32
32
  // OpenCode lacks a real SessionStart hook (#14808 / #5409). The verified
33
33
  // surrogate is experimental.chat.system.transform, which receives the system
34
34
  // prompt array; we inject additionalContext there.
35
- SessionStart: "experimental.chat.system.transform"
35
+ SessionStart: "experimental.chat.system.transform",
36
+ // OpenCode's decision-capable permission gate. The "permission.ask" plugin
37
+ // hook MUTATES output.status ("ask"|"deny"|"allow") rather than returning a
38
+ // value (mirrors tool.execute.before mutating output.args); verified against
39
+ // anomalyco/opencode packages/plugin/src/index.ts.
40
+ PermissionRequest: "permission.ask"
36
41
  };
37
42
  var OpenCodeAdapter = class extends BaseAdapter {
38
43
  id = HOST;
@@ -53,13 +58,13 @@ var OpenCodeAdapter = class extends BaseAdapter {
53
58
  stop: false,
54
59
  notification: false,
55
60
  // Newer events: OpenCode HAS a decision-capable `permission.ask` plugin
56
- // hook upstream, but it is NOT wired here (E1 keeps this adapter
57
- // degrade-only); subagents run as child sessions (bus events only — no
58
- // dedicated lifecycle hook) and tool failure is merged into
59
- // tool.execute.after / session.error. permissionRequest /
60
- // postToolUseFailure / subagentStart / subagentStop stay unset — the
61
- // generated bridge never references them and install reports them as
62
- // "unsupported here".
61
+ // hook upstream now wired (PermissionRequest permission.ask, which
62
+ // mutates output.status). subagents run as child sessions (bus events only
63
+ // — no dedicated lifecycle hook) and tool failure is merged into
64
+ // tool.execute.after / session.error, so postToolUseFailure / subagentStart
65
+ // / subagentStop stay unset — the generated bridge never references them and
66
+ // install reports them as "unsupported here".
67
+ permissionRequest: true,
63
68
  // tool.execute.before mutates output.args → input rewrite supported.
64
69
  canModifyArgs: true,
65
70
  // tool.execute.after mutates output.output → output rewrite supported.
@@ -70,7 +75,14 @@ var OpenCodeAdapter = class extends BaseAdapter {
70
75
  // Content surfaces: OpenCode authors all three natively under <ocDir>.
71
76
  supportsCommands: true,
72
77
  supportsSkills: true,
73
- supportsSubagents: true
78
+ supportsSubagents: true,
79
+ // Native passthrough hooks: OpenCode's host-specific plugin events that have
80
+ // NO normalized HookEventName (chat.message, session.idle, permission.replied,
81
+ // command.execute.before, shell.env, …) are reachable by declaring
82
+ // platforms["opencode"].nativeHooks. The generated plugin emits a bridge
83
+ // registration for each, and the home-bin's runNativeHook dispatches them
84
+ // host-generically (raw payload → handler → verbatim reply).
85
+ supportsNativeHooks: true
74
86
  };
75
87
  // ── Detection ──────────────────────────────────────────────────────────
76
88
  detectInstalled(projectDir) {
@@ -244,23 +256,16 @@ var OpenCodeAdapter = class extends BaseAdapter {
244
256
  // ── Hook install / uninstall (ts-plugin) ───────────────────────────────
245
257
  installHooks(ctx) {
246
258
  const pluginPath = this.getHookConfigPath(ctx);
247
- if (ctx.connector.platforms[HOST]?.hooks === false) {
259
+ const canonicalDisabled = ctx.connector.platforms[HOST]?.hooks === false;
260
+ const hasCanonical = !canonicalDisabled && ctx.connector.hookEvents.length > 0;
261
+ const hasNative = this.nativeHookEvents(ctx).length > 0;
262
+ if (!hasCanonical && !hasNative) {
248
263
  return [
249
264
  {
250
265
  platform: this.id,
251
266
  action: "skip",
252
267
  path: pluginPath,
253
- detail: "hooks disabled for opencode"
254
- }
255
- ];
256
- }
257
- if (ctx.connector.hookEvents.length === 0) {
258
- return [
259
- {
260
- platform: this.id,
261
- action: "skip",
262
- path: pluginPath,
263
- detail: "connector declares no hooks"
268
+ detail: canonicalDisabled ? "hooks disabled for opencode" : "connector declares no hooks"
264
269
  }
265
270
  ];
266
271
  }
@@ -293,12 +298,25 @@ var OpenCodeAdapter = class extends BaseAdapter {
293
298
  * as "unsupported here" so the detail never overstates coverage.
294
299
  */
295
300
  hookDetail(ctx) {
296
- const declared = ctx.connector.hookEvents;
301
+ const declared = ctx.connector.platforms[HOST]?.hooks === false ? [] : ctx.connector.hookEvents;
297
302
  const mapped = declared.filter((e) => EVENT_TO_OPENCODE[e] !== void 0);
298
303
  const unsupported = declared.filter((e) => EVENT_TO_OPENCODE[e] === void 0);
299
- const base = mapped.join(",");
304
+ const native = this.nativeHookEvents(ctx);
305
+ const parts = [];
306
+ if (mapped.length > 0) parts.push(mapped.join(","));
307
+ if (native.length > 0) parts.push(`native: ${native.join(",")}`);
308
+ const base = parts.join("; ") || "no canonical hooks";
300
309
  return unsupported.length > 0 ? `${base}; unsupported here: ${unsupported.join(",")}` : base;
301
310
  }
311
+ /**
312
+ * OpenCode-native passthrough events declared on platforms["opencode"]
313
+ * .nativeHooks (host event names with no normalized HookEventName, e.g.
314
+ * session.idle / chat.message). Emitted as generic bridge registrations and
315
+ * dispatched host-generically by the home-bin's runNativeHook.
316
+ */
317
+ nativeHookEvents(ctx) {
318
+ return Object.keys(ctx.connector.platforms[HOST]?.nativeHooks ?? {});
319
+ }
302
320
  uninstallHooks(ctx) {
303
321
  const pluginPath = this.getHookConfigPath(ctx);
304
322
  if (!existsSync(pluginPath)) {
@@ -529,7 +547,8 @@ var OpenCodeAdapter = class extends BaseAdapter {
529
547
  buildPluginSource(ctx) {
530
548
  const homeBin = JSON.stringify(ctx.homeBinPath);
531
549
  const connectorId = JSON.stringify(ctx.connector.id);
532
- const events = ctx.connector.hookEvents.filter(
550
+ const canonicalDisabled = ctx.connector.platforms[HOST]?.hooks === false;
551
+ const events = canonicalDisabled ? [] : ctx.connector.hookEvents.filter(
533
552
  (e) => EVENT_TO_OPENCODE[e] !== void 0
534
553
  );
535
554
  const has = (e) => events.includes(e);
@@ -628,6 +647,36 @@ function bridge(event, payload) {
628
647
  }
629
648
  },`);
630
649
  }
650
+ if (has("PermissionRequest")) {
651
+ handlers.push(` // PermissionRequest \u2192 decision-capable permission gate.
652
+ // OpenCode's "permission.ask" MUTATES output.status ("ask"|"deny"|"allow")
653
+ // \u2014 it does NOT return a value (mirrors tool.execute.before/output.args).
654
+ // A normalized "deny" maps to output.status "deny"; "ask" to "ask";
655
+ // allow/void leave the default status untouched.
656
+ "permission.ask": async (input, output) => {
657
+ const payload = {
658
+ toolName: (input && (input.type || input.tool)) ?? "",
659
+ toolInput: (input && (input.metadata || input.args)) ?? {},
660
+ sessionId: (input && input.sessionID) ?? "",
661
+ projectDir: PROJECT_DIR,
662
+ };
663
+ const res = bridge("PermissionRequest", payload);
664
+ if (!res || !output) return;
665
+ if (res.decision === "deny") output.status = "deny";
666
+ else if (res.decision === "ask") output.status = "ask";
667
+ },`);
668
+ }
669
+ for (const nativeEvent of this.nativeHookEvents(ctx)) {
670
+ const key = JSON.stringify(nativeEvent);
671
+ handlers.push(` // native passthrough \u2192 ${nativeEvent} (runNativeHook host-generic dispatch).
672
+ [${key}]: async (input, output) => {
673
+ bridge(${key}, {
674
+ sessionId: (input && input.sessionID) ?? "",
675
+ projectDir: PROJECT_DIR,
676
+ raw: input,
677
+ });
678
+ },`);
679
+ }
631
680
  const factory = `
632
681
  export default async function (ctx) {
633
682
  // ctx.directory is the OpenCode project root; fall back to cwd.
@@ -676,6 +725,14 @@ ${handlers.join("\n")}
676
725
  };
677
726
  return ev;
678
727
  }
728
+ case "PermissionRequest": {
729
+ const ev = {
730
+ ...base,
731
+ toolName: input.toolName ?? "",
732
+ toolInput: input.toolInput ?? {}
733
+ };
734
+ return ev;
735
+ }
679
736
  case "SessionStart": {
680
737
  const ev = { ...base, source: "startup" };
681
738
  return ev;
@@ -777,4 +834,4 @@ export {
777
834
  adapter,
778
835
  opencode_default as default
779
836
  };
780
- //# sourceMappingURL=opencode-Q6KBSAKW.js.map
837
+ //# sourceMappingURL=opencode-WNXYJR4X.js.map