@mxalbert/context-mode 2.0.0

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 (365) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +31 -0
  3. package/.codex-plugin/hooks.json +65 -0
  4. package/.codex-plugin/mcp.json +13 -0
  5. package/.codex-plugin/plugin.json +31 -0
  6. package/.openclaw-plugin/index.ts +12 -0
  7. package/.openclaw-plugin/openclaw.plugin.json +38 -0
  8. package/.openclaw-plugin/package.json +28 -0
  9. package/LICENSE +94 -0
  10. package/README.md +1619 -0
  11. package/bin/statusline.mjs +378 -0
  12. package/build/adapters/antigravity/index.d.ts +51 -0
  13. package/build/adapters/antigravity/index.js +169 -0
  14. package/build/adapters/antigravity-cli/index.d.ts +51 -0
  15. package/build/adapters/antigravity-cli/index.js +342 -0
  16. package/build/adapters/base.d.ts +92 -0
  17. package/build/adapters/base.js +135 -0
  18. package/build/adapters/claude-code/config.d.ts +8 -0
  19. package/build/adapters/claude-code/config.js +8 -0
  20. package/build/adapters/claude-code/hooks.d.ts +106 -0
  21. package/build/adapters/claude-code/hooks.js +183 -0
  22. package/build/adapters/claude-code/index.d.ts +80 -0
  23. package/build/adapters/claude-code/index.js +569 -0
  24. package/build/adapters/claude-code-base.d.ts +49 -0
  25. package/build/adapters/claude-code-base.js +113 -0
  26. package/build/adapters/client-map.d.ts +10 -0
  27. package/build/adapters/client-map.js +42 -0
  28. package/build/adapters/codex/config.d.ts +8 -0
  29. package/build/adapters/codex/config.js +8 -0
  30. package/build/adapters/codex/hooks.d.ts +55 -0
  31. package/build/adapters/codex/hooks.js +64 -0
  32. package/build/adapters/codex/index.d.ts +87 -0
  33. package/build/adapters/codex/index.js +995 -0
  34. package/build/adapters/codex/paths.d.ts +1 -0
  35. package/build/adapters/codex/paths.js +12 -0
  36. package/build/adapters/codex/usage.d.ts +107 -0
  37. package/build/adapters/codex/usage.js +227 -0
  38. package/build/adapters/copilot-base.d.ts +94 -0
  39. package/build/adapters/copilot-base.js +297 -0
  40. package/build/adapters/copilot-cli/hooks.d.ts +33 -0
  41. package/build/adapters/copilot-cli/hooks.js +64 -0
  42. package/build/adapters/copilot-cli/index.d.ts +48 -0
  43. package/build/adapters/copilot-cli/index.js +341 -0
  44. package/build/adapters/cursor/config.d.ts +4 -0
  45. package/build/adapters/cursor/config.js +4 -0
  46. package/build/adapters/cursor/hooks.d.ts +55 -0
  47. package/build/adapters/cursor/hooks.js +82 -0
  48. package/build/adapters/cursor/index.d.ts +64 -0
  49. package/build/adapters/cursor/index.js +517 -0
  50. package/build/adapters/detect.d.ts +136 -0
  51. package/build/adapters/detect.js +623 -0
  52. package/build/adapters/gemini-cli/config.d.ts +8 -0
  53. package/build/adapters/gemini-cli/config.js +8 -0
  54. package/build/adapters/gemini-cli/hooks.d.ts +83 -0
  55. package/build/adapters/gemini-cli/hooks.js +115 -0
  56. package/build/adapters/gemini-cli/index.d.ts +80 -0
  57. package/build/adapters/gemini-cli/index.js +483 -0
  58. package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
  59. package/build/adapters/jetbrains-copilot/config.js +8 -0
  60. package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
  61. package/build/adapters/jetbrains-copilot/hooks.js +87 -0
  62. package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
  63. package/build/adapters/jetbrains-copilot/index.js +131 -0
  64. package/build/adapters/kimi/config.d.ts +8 -0
  65. package/build/adapters/kimi/config.js +8 -0
  66. package/build/adapters/kimi/hooks.d.ts +28 -0
  67. package/build/adapters/kimi/hooks.js +34 -0
  68. package/build/adapters/kimi/index.d.ts +66 -0
  69. package/build/adapters/kimi/index.js +537 -0
  70. package/build/adapters/kimi/paths.d.ts +21 -0
  71. package/build/adapters/kimi/paths.js +52 -0
  72. package/build/adapters/kimi/usage.d.ts +82 -0
  73. package/build/adapters/kimi/usage.js +217 -0
  74. package/build/adapters/kiro/hooks.d.ts +61 -0
  75. package/build/adapters/kiro/hooks.js +94 -0
  76. package/build/adapters/kiro/index.d.ts +61 -0
  77. package/build/adapters/kiro/index.js +301 -0
  78. package/build/adapters/omp/index.d.ts +65 -0
  79. package/build/adapters/omp/index.js +182 -0
  80. package/build/adapters/omp/plugin.d.ts +81 -0
  81. package/build/adapters/omp/plugin.js +331 -0
  82. package/build/adapters/omp/usage.d.ts +49 -0
  83. package/build/adapters/omp/usage.js +110 -0
  84. package/build/adapters/openclaw/config.d.ts +8 -0
  85. package/build/adapters/openclaw/config.js +8 -0
  86. package/build/adapters/openclaw/hooks.d.ts +50 -0
  87. package/build/adapters/openclaw/hooks.js +61 -0
  88. package/build/adapters/openclaw/index.d.ts +74 -0
  89. package/build/adapters/openclaw/index.js +439 -0
  90. package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
  91. package/build/adapters/openclaw/mcp-tools.js +207 -0
  92. package/build/adapters/openclaw/plugin.d.ts +141 -0
  93. package/build/adapters/openclaw/plugin.js +818 -0
  94. package/build/adapters/openclaw/session-db.d.ts +55 -0
  95. package/build/adapters/openclaw/session-db.js +88 -0
  96. package/build/adapters/openclaw/usage.d.ts +34 -0
  97. package/build/adapters/openclaw/usage.js +52 -0
  98. package/build/adapters/openclaw/workspace-router.d.ts +29 -0
  99. package/build/adapters/openclaw/workspace-router.js +64 -0
  100. package/build/adapters/opencode/config.d.ts +8 -0
  101. package/build/adapters/opencode/config.js +8 -0
  102. package/build/adapters/opencode/hooks.d.ts +38 -0
  103. package/build/adapters/opencode/hooks.js +50 -0
  104. package/build/adapters/opencode/index.d.ts +144 -0
  105. package/build/adapters/opencode/index.js +638 -0
  106. package/build/adapters/opencode/plugin.d.ts +249 -0
  107. package/build/adapters/opencode/plugin.js +1536 -0
  108. package/build/adapters/opencode/v2.d.ts +117 -0
  109. package/build/adapters/opencode/v2.js +224 -0
  110. package/build/adapters/opencode/zod3tov4.d.ts +1 -0
  111. package/build/adapters/opencode/zod3tov4.js +132 -0
  112. package/build/adapters/pi/extension.d.ts +81 -0
  113. package/build/adapters/pi/extension.js +861 -0
  114. package/build/adapters/pi/index.d.ts +57 -0
  115. package/build/adapters/pi/index.js +173 -0
  116. package/build/adapters/pi/mcp-bridge.d.ts +268 -0
  117. package/build/adapters/pi/mcp-bridge.js +894 -0
  118. package/build/adapters/qwen-code/hooks.d.ts +26 -0
  119. package/build/adapters/qwen-code/hooks.js +29 -0
  120. package/build/adapters/qwen-code/index.d.ts +40 -0
  121. package/build/adapters/qwen-code/index.js +361 -0
  122. package/build/adapters/qwen-code/usage.d.ts +90 -0
  123. package/build/adapters/qwen-code/usage.js +222 -0
  124. package/build/adapters/types.d.ts +360 -0
  125. package/build/adapters/types.js +118 -0
  126. package/build/adapters/vscode-copilot/config.d.ts +8 -0
  127. package/build/adapters/vscode-copilot/config.js +8 -0
  128. package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
  129. package/build/adapters/vscode-copilot/hooks.js +98 -0
  130. package/build/adapters/vscode-copilot/index.d.ts +32 -0
  131. package/build/adapters/vscode-copilot/index.js +222 -0
  132. package/build/adapters/zed/index.d.ts +40 -0
  133. package/build/adapters/zed/index.js +163 -0
  134. package/build/cli.d.ts +34 -0
  135. package/build/cli.js +1828 -0
  136. package/build/db-base.d.ts +250 -0
  137. package/build/db-base.js +756 -0
  138. package/build/executor.d.ts +72 -0
  139. package/build/executor.js +678 -0
  140. package/build/exit-classify.d.ts +19 -0
  141. package/build/exit-classify.js +12 -0
  142. package/build/fetch/blocks.d.ts +145 -0
  143. package/build/fetch/blocks.js +244 -0
  144. package/build/fetch/extract.d.ts +68 -0
  145. package/build/fetch/extract.js +123 -0
  146. package/build/fetch/page-store.d.ts +66 -0
  147. package/build/fetch/page-store.js +174 -0
  148. package/build/fetch-cache.d.ts +13 -0
  149. package/build/fetch-cache.js +15 -0
  150. package/build/lifecycle.d.ts +124 -0
  151. package/build/lifecycle.js +278 -0
  152. package/build/package-identity.d.ts +26 -0
  153. package/build/package-identity.js +28 -0
  154. package/build/runPool.d.ts +36 -0
  155. package/build/runPool.js +51 -0
  156. package/build/runtime.d.ts +71 -0
  157. package/build/runtime.js +659 -0
  158. package/build/search/auto-memory.d.ts +47 -0
  159. package/build/search/auto-memory.js +170 -0
  160. package/build/search/ctx-search-schema.d.ts +90 -0
  161. package/build/search/ctx-search-schema.js +135 -0
  162. package/build/search/flood-guard.d.ts +57 -0
  163. package/build/search/flood-guard.js +80 -0
  164. package/build/search/unified.d.ts +56 -0
  165. package/build/search/unified.js +104 -0
  166. package/build/security.d.ts +206 -0
  167. package/build/security.js +780 -0
  168. package/build/server.d.ts +240 -0
  169. package/build/server.js +5034 -0
  170. package/build/session/analytics.d.ts +773 -0
  171. package/build/session/analytics.js +2420 -0
  172. package/build/session/db.d.ts +468 -0
  173. package/build/session/db.js +1330 -0
  174. package/build/session/error-classifier.d.ts +87 -0
  175. package/build/session/error-classifier.js +303 -0
  176. package/build/session/event-emit.d.ts +48 -0
  177. package/build/session/event-emit.js +101 -0
  178. package/build/session/extract.d.ts +333 -0
  179. package/build/session/extract.js +2688 -0
  180. package/build/session/model-prices.json +429 -0
  181. package/build/session/persist-tool-calls.d.ts +54 -0
  182. package/build/session/persist-tool-calls.js +105 -0
  183. package/build/session/pricing.d.ts +64 -0
  184. package/build/session/pricing.js +151 -0
  185. package/build/session/project-attribution.d.ts +73 -0
  186. package/build/session/project-attribution.js +245 -0
  187. package/build/session/purge.d.ts +138 -0
  188. package/build/session/purge.js +240 -0
  189. package/build/session/retrieval-marker.d.ts +39 -0
  190. package/build/session/retrieval-marker.js +65 -0
  191. package/build/session/snapshot.d.ts +46 -0
  192. package/build/session/snapshot.js +532 -0
  193. package/build/store-directory.d.ts +56 -0
  194. package/build/store-directory.js +254 -0
  195. package/build/store.d.ts +147 -0
  196. package/build/store.js +1714 -0
  197. package/build/truncate.d.ts +57 -0
  198. package/build/truncate.js +142 -0
  199. package/build/types.d.ts +117 -0
  200. package/build/types.js +20 -0
  201. package/build/util/claude-config.d.ts +32 -0
  202. package/build/util/claude-config.js +84 -0
  203. package/build/util/hook-config.d.ts +27 -0
  204. package/build/util/hook-config.js +76 -0
  205. package/build/util/jsonc.d.ts +14 -0
  206. package/build/util/jsonc.js +104 -0
  207. package/build/util/plugin-cache-integrity.d.ts +51 -0
  208. package/build/util/plugin-cache-integrity.js +146 -0
  209. package/build/util/project-dir.d.ts +149 -0
  210. package/build/util/project-dir.js +340 -0
  211. package/build/util/sibling-mcp.d.ts +79 -0
  212. package/build/util/sibling-mcp.js +181 -0
  213. package/cli.bundle.mjs +1495 -0
  214. package/configs/antigravity/GEMINI.md +68 -0
  215. package/configs/antigravity/mcp_config.json +7 -0
  216. package/configs/antigravity-cli/hooks/hooks.json +37 -0
  217. package/configs/antigravity-cli/hooks.json +37 -0
  218. package/configs/antigravity-cli/mcp_config.json +10 -0
  219. package/configs/antigravity-cli/plugin.json +14 -0
  220. package/configs/antigravity-cli/rules/context-mode.md +77 -0
  221. package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
  222. package/configs/claude-code/CLAUDE.md +91 -0
  223. package/configs/codex/AGENTS.md +99 -0
  224. package/configs/codex/config.toml +8 -0
  225. package/configs/codex/hooks.json +47 -0
  226. package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
  227. package/configs/copilot-cli/.mcp.json +12 -0
  228. package/configs/copilot-cli/README.md +47 -0
  229. package/configs/copilot-cli/hooks.json +41 -0
  230. package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
  231. package/configs/cursor/context-mode.mdc +76 -0
  232. package/configs/cursor/hooks.json +21 -0
  233. package/configs/cursor/mcp.json +7 -0
  234. package/configs/gemini-cli/GEMINI.md +88 -0
  235. package/configs/gemini-cli/mcp.json +7 -0
  236. package/configs/gemini-cli/settings.json +60 -0
  237. package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
  238. package/configs/jetbrains-copilot/hooks.json +16 -0
  239. package/configs/jetbrains-copilot/mcp.json +7 -0
  240. package/configs/kilo/AGENTS.md +84 -0
  241. package/configs/kilo/kilo.json +6 -0
  242. package/configs/kimi/hooks.json +54 -0
  243. package/configs/kiro/KIRO.md +84 -0
  244. package/configs/kiro/agent.json +18 -0
  245. package/configs/kiro/mcp.json +7 -0
  246. package/configs/omp/SYSTEM.md +85 -0
  247. package/configs/omp/mcp.json +7 -0
  248. package/configs/openclaw/AGENTS.md +87 -0
  249. package/configs/openclaw/openclaw.json +13 -0
  250. package/configs/opencode/AGENTS.md +84 -0
  251. package/configs/opencode/opencode.json +6 -0
  252. package/configs/pi/AGENTS.md +3 -0
  253. package/configs/qwen-code/QWEN.md +91 -0
  254. package/configs/vscode-copilot/copilot-instructions.md +80 -0
  255. package/configs/vscode-copilot/hooks.json +16 -0
  256. package/configs/vscode-copilot/mcp.json +7 -0
  257. package/configs/zed/AGENTS.md +68 -0
  258. package/hooks/antigravity-cli/payload.mjs +98 -0
  259. package/hooks/antigravity-cli/posttooluse.mjs +138 -0
  260. package/hooks/antigravity-cli/pretooluse.mjs +78 -0
  261. package/hooks/antigravity-cli/stop.mjs +58 -0
  262. package/hooks/auto-injection.mjs +102 -0
  263. package/hooks/cache-heal-utils.mjs +379 -0
  264. package/hooks/codex/platform.mjs +1 -0
  265. package/hooks/codex/posttooluse.mjs +66 -0
  266. package/hooks/codex/precompact.mjs +70 -0
  267. package/hooks/codex/pretooluse.mjs +42 -0
  268. package/hooks/codex/sessionstart.mjs +121 -0
  269. package/hooks/codex/stop.mjs +138 -0
  270. package/hooks/codex/userpromptsubmit.mjs +75 -0
  271. package/hooks/copilot-cli/posttooluse.mjs +79 -0
  272. package/hooks/copilot-cli/precompact.mjs +66 -0
  273. package/hooks/copilot-cli/pretooluse.mjs +41 -0
  274. package/hooks/copilot-cli/sessionstart.mjs +121 -0
  275. package/hooks/copilot-cli/stop.mjs +59 -0
  276. package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
  277. package/hooks/core/codex-caps.mjs +112 -0
  278. package/hooks/core/formatters.mjs +356 -0
  279. package/hooks/core/mcp-ready.mjs +109 -0
  280. package/hooks/core/platform-detect.mjs +49 -0
  281. package/hooks/core/routing.mjs +1050 -0
  282. package/hooks/core/stdin.mjs +90 -0
  283. package/hooks/core/tool-naming.mjs +57 -0
  284. package/hooks/cursor/afteragentresponse.mjs +74 -0
  285. package/hooks/cursor/hooks.json +31 -0
  286. package/hooks/cursor/posttooluse.mjs +72 -0
  287. package/hooks/cursor/pretooluse.mjs +26 -0
  288. package/hooks/cursor/sessionstart.mjs +97 -0
  289. package/hooks/cursor/stop.mjs +48 -0
  290. package/hooks/ensure-deps.mjs +254 -0
  291. package/hooks/formatters/claude-code.mjs +113 -0
  292. package/hooks/formatters/cursor.mjs +37 -0
  293. package/hooks/formatters/gemini-cli.mjs +55 -0
  294. package/hooks/formatters/vscode-copilot.mjs +55 -0
  295. package/hooks/gemini-cli/aftermodel.mjs +70 -0
  296. package/hooks/gemini-cli/aftertool.mjs +61 -0
  297. package/hooks/gemini-cli/beforeagent.mjs +99 -0
  298. package/hooks/gemini-cli/beforetool.mjs +27 -0
  299. package/hooks/gemini-cli/precompress.mjs +55 -0
  300. package/hooks/gemini-cli/sessionstart.mjs +137 -0
  301. package/hooks/heal-partial-install.mjs +712 -0
  302. package/hooks/hooks.json +143 -0
  303. package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
  304. package/hooks/jetbrains-copilot/precompact.mjs +55 -0
  305. package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
  306. package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
  307. package/hooks/kimi/platform.mjs +1 -0
  308. package/hooks/kimi/posttooluse.mjs +72 -0
  309. package/hooks/kimi/precompact.mjs +80 -0
  310. package/hooks/kimi/pretooluse.mjs +42 -0
  311. package/hooks/kimi/sessionend.mjs +61 -0
  312. package/hooks/kimi/sessionstart.mjs +113 -0
  313. package/hooks/kimi/stop.mjs +132 -0
  314. package/hooks/kimi/userpromptsubmit.mjs +90 -0
  315. package/hooks/kiro/agentspawn.mjs +97 -0
  316. package/hooks/kiro/posttooluse.mjs +51 -0
  317. package/hooks/kiro/pretooluse.mjs +64 -0
  318. package/hooks/kiro/userpromptsubmit.mjs +88 -0
  319. package/hooks/normalize-hooks.mjs +323 -0
  320. package/hooks/platform-bridge.mjs +340 -0
  321. package/hooks/posttooluse.mjs +225 -0
  322. package/hooks/precompact.mjs +100 -0
  323. package/hooks/pretooluse.mjs +227 -0
  324. package/hooks/qwen-code/platform.mjs +1 -0
  325. package/hooks/qwen-code/stop.mjs +168 -0
  326. package/hooks/routing-block.mjs +105 -0
  327. package/hooks/run-hook.mjs +95 -0
  328. package/hooks/security.bundle.mjs +2 -0
  329. package/hooks/session-attribution.bundle.mjs +1 -0
  330. package/hooks/session-db.bundle.mjs +145 -0
  331. package/hooks/session-directive.mjs +520 -0
  332. package/hooks/session-extract.bundle.mjs +3 -0
  333. package/hooks/session-helpers.mjs +428 -0
  334. package/hooks/session-loaders.mjs +448 -0
  335. package/hooks/session-snapshot.bundle.mjs +32 -0
  336. package/hooks/sessionstart.mjs +464 -0
  337. package/hooks/stop.mjs +82 -0
  338. package/hooks/suppress-stderr.mjs +25 -0
  339. package/hooks/userpromptsubmit.mjs +108 -0
  340. package/hooks/vscode-copilot/posttooluse.mjs +61 -0
  341. package/hooks/vscode-copilot/precompact.mjs +55 -0
  342. package/hooks/vscode-copilot/pretooluse.mjs +27 -0
  343. package/hooks/vscode-copilot/sessionstart.mjs +129 -0
  344. package/openclaw.plugin.json +38 -0
  345. package/package.json +132 -0
  346. package/scripts/heal-better-sqlite3.mjs +430 -0
  347. package/scripts/heal-installed-plugins.mjs +635 -0
  348. package/scripts/plugin-cache-integrity.mjs +248 -0
  349. package/scripts/postinstall.mjs +397 -0
  350. package/server.bundle.mjs +1421 -0
  351. package/server.js +5 -0
  352. package/skills/.ignore +7 -0
  353. package/skills/context-mode/SKILL.md +300 -0
  354. package/skills/context-mode/references/anti-patterns.md +283 -0
  355. package/skills/context-mode/references/patterns-javascript.md +298 -0
  356. package/skills/context-mode/references/patterns-python.md +304 -0
  357. package/skills/context-mode/references/patterns-shell.md +277 -0
  358. package/skills/ctx-doctor/SKILL.md +22 -0
  359. package/skills/ctx-index/SKILL.md +46 -0
  360. package/skills/ctx-insight/SKILL.md +24 -0
  361. package/skills/ctx-purge/SKILL.md +49 -0
  362. package/skills/ctx-search/SKILL.md +35 -0
  363. package/skills/ctx-stats/SKILL.md +26 -0
  364. package/skills/ctx-upgrade/SKILL.md +31 -0
  365. package/start.mjs +616 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Oh My Pi (OMP) plugin entry point for context-mode.
3
+ *
4
+ * Mirrors the Pi extension shape (`src/adapters/pi/extension.ts`) for
5
+ * the four OMP hook events that materially protect the context window
6
+ * and persist session continuity:
7
+ *
8
+ * - session_start — initialize the session row in our DB
9
+ * - tool_call — hard-block curl/wget/inline-HTTP in bash
10
+ * - tool_result — extract structured events into the session DB
11
+ * - session_before_compact — persist a resume snapshot before compaction
12
+ *
13
+ * Loaded by OMP via the `omp` (or `pi`) field in package.json — see
14
+ * upstream loader at refs/platforms/oh-my-pi/packages/coding-agent/src/
15
+ * extensibility/plugins/loader.ts:75:
16
+ * `const manifest: PluginManifest | undefined = pluginPkg.omp || pluginPkg.pi;`
17
+ * Hook factory contract from refs/.../extensibility/hooks/types.ts:809:
18
+ * `export type HookFactory = (pi: HookAPI) => void;`
19
+ *
20
+ * OMP differs from Pi in two ways that justify a dedicated plugin file:
21
+ * 1. Storage roots at ~/.omp/context-mode/ via OMPAdapter, not ~/.pi/
22
+ * 2. OMP has native MCP support (mcp.json), so no MCP bridge is needed
23
+ * — the bridge that Pi's extension ships (mcp-bridge.ts) is dead weight
24
+ * under OMP and is intentionally omitted here.
25
+ */
26
+ import { createHash } from "node:crypto";
27
+ import { existsSync, mkdirSync } from "node:fs";
28
+ import { dirname, resolve } from "node:path";
29
+ import { fileURLToPath } from "node:url";
30
+ import { resolveSessionDbPath, SessionDB } from "../../session/db.js";
31
+ import { extractEvents, buildAgentUsageEvent } from "../../session/extract.js";
32
+ import { buildResumeSnapshot } from "../../session/snapshot.js";
33
+ import { OMPAdapter } from "./index.js";
34
+ import { parseOmpUsage } from "./usage.js";
35
+ // ── Tool-name normalization ─────────────────────────────
36
+ // OMP uses lowercase tool names (refs/.../hooks/types.ts:451 example
37
+ // `toolName: "bash"`). Shared event extractors expect PascalCase
38
+ // (Claude Code convention). Map the common ones.
39
+ const OMP_TOOL_MAP = {
40
+ bash: "Bash",
41
+ edit: "Edit",
42
+ read: "Read",
43
+ write: "Write",
44
+ list: "Glob",
45
+ view: "Read",
46
+ };
47
+ // ── Routing patterns ─────────────────────────────────────
48
+ // Inline HTTP client patterns to hard-block in bash. Identical to the
49
+ // Pi extension list (src/adapters/pi/extension.ts:42). One unrouted
50
+ // curl can dump 56 KB into context.
51
+ const BLOCKED_BASH_PATTERNS = [
52
+ /\bcurl\s/,
53
+ /\bwget\s/,
54
+ /\bfetch\s*\(/,
55
+ /\brequests\.get\s*\(/,
56
+ /\brequests\.post\s*\(/,
57
+ /\bhttp\.get\s*\(/,
58
+ /\bhttp\.request\s*\(/,
59
+ /\burllib\.request/,
60
+ /\bInvoke-WebRequest\b/,
61
+ ];
62
+ // ── Module-level singletons ──────────────────────────────
63
+ // Same shape as Pi: one DB per process, session ID rebound on each
64
+ // session_start so multi-session reuse within a long-lived plugin
65
+ // process keeps event attribution correct.
66
+ let _db = null;
67
+ let _dbPath = "";
68
+ let _sessionId = "";
69
+ const _ompAdapter = new OMPAdapter();
70
+ // ── MCP self-registration (issue #677) ───────────────────
71
+ // The `omp plugin install context-mode` path wires THIS extension factory
72
+ // (so routing hooks fire), but never creates the MCP config — so the 11
73
+ // `ctx_*` tools stay unreachable even though curl/wget are blocked. Register
74
+ // the server ourselves on plugin load, ONLY when absent (never clobber a
75
+ // user's existing entry). Takes effect on the next OMP restart, same as the
76
+ // manual mcp.json workaround the issue documents.
77
+ const MCP_SERVER_NAME = "context-mode";
78
+ // plugin.js ships at <pkg>/build/adapters/omp/plugin.js; the MCP server
79
+ // bundle sits at the package root (<pkg>/server.bundle.mjs) — three up.
80
+ const SERVER_BUNDLE_RELATIVE = "../../../server.bundle.mjs";
81
+ function resolveServerBundle() {
82
+ try {
83
+ const here = dirname(fileURLToPath(import.meta.url));
84
+ const bundle = resolve(here, SERVER_BUNDLE_RELATIVE);
85
+ return existsSync(bundle) ? bundle : null;
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ /**
92
+ * Ensure `~/.omp/agent/mcp.json` registers the context-mode MCP server.
93
+ *
94
+ * Uses `node <abs>/server.bundle.mjs` rather than the `context-mode` bin:
95
+ * under the plugin install the package lives in `~/.omp/plugins/node_modules`
96
+ * and its bin is NOT on PATH, so the bare command would fail to spawn (the
97
+ * exact symptom reported on issue #677). Best effort — never throws, never
98
+ * breaks plugin load.
99
+ */
100
+ function ensureMcpServerRegistered() {
101
+ try {
102
+ const bundle = resolveServerBundle();
103
+ if (!bundle)
104
+ return; // bundle missing → nothing safe to register
105
+ const settings = _ompAdapter.readSettings() ?? {};
106
+ const mcpServers = settings.mcpServers ?? {};
107
+ if (MCP_SERVER_NAME in mcpServers)
108
+ return; // already present — don't clobber
109
+ mcpServers[MCP_SERVER_NAME] = {
110
+ type: "stdio",
111
+ command: "node",
112
+ args: [bundle],
113
+ };
114
+ settings.mcpServers = mcpServers;
115
+ _ompAdapter.writeSettings(settings);
116
+ }
117
+ catch {
118
+ // best effort — a registration failure must never break plugin load
119
+ }
120
+ }
121
+ function getSessionDir() {
122
+ const dir = _ompAdapter.getSessionDir();
123
+ mkdirSync(dir, { recursive: true });
124
+ return dir;
125
+ }
126
+ // Issue #645 — route through the canonical per-project resolver the MCP
127
+ // server uses (src/server.ts ctx_stats / ctx_search timeline). The
128
+ // previous shared `context-mode.db` literal was a different file from
129
+ // the `<canonical-hash>.db` the server reads, so every OMP user's
130
+ // `ctx_stats` reported zero history and `ctx_search(sort: "timeline")`
131
+ // silently dropped the sort. Mirrors the matching Pi fix and the
132
+ // opencode plugin pattern (src/adapters/opencode/plugin.ts:307).
133
+ function getDBPath(projectDir) {
134
+ return resolveSessionDbPath({ projectDir, sessionsDir: getSessionDir() });
135
+ }
136
+ function getOrCreateDB(projectDir) {
137
+ // Reopen the singleton if the resolved DB path changes. See the
138
+ // matching Pi extension comment — defensive re-keying on projectDir
139
+ // hash keeps tests deterministic and stops a stale singleton from
140
+ // pointing at an earlier projectDir's `<hash>.db`. (#645)
141
+ const dbPath = getDBPath(projectDir);
142
+ if (!_db || _dbPath !== dbPath) {
143
+ if (_db) {
144
+ try {
145
+ _db.close();
146
+ }
147
+ catch { /* best effort */ }
148
+ }
149
+ _db = new SessionDB({ dbPath });
150
+ _dbPath = dbPath;
151
+ }
152
+ return _db;
153
+ }
154
+ /**
155
+ * Derive a stable session ID from OMP's session manager when available,
156
+ * otherwise fall back to a wall-clock token. Mirrors the Pi extension
157
+ * derivation (src/adapters/pi/extension.ts:142) — the OMP `ctx` object
158
+ * exposes `sessionManager.getSessionFile()` per refs/.../hooks/types.ts.
159
+ */
160
+ function deriveSessionId(ctx) {
161
+ try {
162
+ const sessionManager = ctx
163
+ ?.sessionManager;
164
+ const sessionFile = sessionManager?.getSessionFile?.();
165
+ if (sessionFile && typeof sessionFile === "string") {
166
+ return createHash("sha256").update(sessionFile).digest("hex").slice(0, 16);
167
+ }
168
+ }
169
+ catch {
170
+ // best effort
171
+ }
172
+ return `omp-${Date.now()}`;
173
+ }
174
+ // ── Test-only state reset (NOT exported via plugin entry) ───────────
175
+ // The plugin's default export is the OMP factory; this helper is only
176
+ // imported by tests to clear singletons between cases.
177
+ export function _resetOmpPluginStateForTests() {
178
+ if (_db) {
179
+ try {
180
+ _db.close();
181
+ }
182
+ catch { /* best effort */ }
183
+ }
184
+ _db = null;
185
+ _dbPath = "";
186
+ _sessionId = "";
187
+ }
188
+ /**
189
+ * Return the current session ID picked by the most recent session_start
190
+ * handler. Test-only — production code reads `_sessionId` directly via
191
+ * the closure. The shared SQLite DB at `~/.omp/context-mode/` survives
192
+ * between tests, so `getLatestSessionId()` cannot disambiguate which
193
+ * row belongs to "this" test when multiple tests insert in the same
194
+ * second; tests use this getter instead.
195
+ */
196
+ export function _getOmpPluginSessionIdForTests() {
197
+ return _sessionId;
198
+ }
199
+ // ── Plugin entry point ───────────────────────────────────
200
+ /**
201
+ * OMP plugin default export. Called once by the OMP runtime per
202
+ * upstream `extensibility/plugins/loader.ts` after `omp plugin install
203
+ * context-mode`. Subsequent `pi.on(...)` registrations route the four
204
+ * lifecycle events to our SessionDB-backed handlers below.
205
+ */
206
+ export default function ompPlugin(pi) {
207
+ // OMP upstream uses PI_-prefixed env vars only (verified against
208
+ // can1357/oh-my-pi v3.20.1 — see `packages/utils/src/dirs.ts`). The
209
+ // earlier `OMP_PROJECT_DIR` read was an EM mistake — no upstream code
210
+ // ever sets it. Drop it; fall through PI_PROJECT_DIR → cwd().
211
+ const projectDir = process.env.PI_PROJECT_DIR || process.cwd();
212
+ // Self-register the MCP server so `ctx_*` tools are reachable under the
213
+ // plugin install path, not just the manual MCP-only path (issue #677).
214
+ ensureMcpServerRegistered();
215
+ const db = getOrCreateDB(projectDir);
216
+ // ── 1. session_start — initialize session row ─────────
217
+ pi.on("session_start", (_event, ctx) => {
218
+ try {
219
+ _sessionId = deriveSessionId(ctx);
220
+ db.ensureSession(_sessionId, projectDir);
221
+ db.cleanupOldSessions(7);
222
+ }
223
+ catch {
224
+ // best effort — never break session start
225
+ if (!_sessionId) {
226
+ _sessionId = `omp-${Date.now()}`;
227
+ }
228
+ }
229
+ return undefined;
230
+ });
231
+ // ── 2. tool_call — pre-tool-call hard-block ───────────
232
+ // Returning `{block: true, reason}` per
233
+ // refs/.../hooks/types.ts:566 (ToolCallEventResult) terminates the
234
+ // tool call with the reason surfaced to the LLM.
235
+ pi.on("tool_call", (event) => {
236
+ try {
237
+ const toolName = String(event?.toolName ?? "").toLowerCase();
238
+ if (toolName !== "bash")
239
+ return undefined;
240
+ const command = String(event?.input?.command ?? "");
241
+ if (!command)
242
+ return undefined;
243
+ const isBlocked = BLOCKED_BASH_PATTERNS.some((p) => p.test(command));
244
+ if (isBlocked) {
245
+ return {
246
+ block: true,
247
+ reason: "Use context-mode MCP tools (ctx_execute, ctx_fetch_and_index) instead of inline HTTP. " +
248
+ "curl/wget/fetch dump raw HTTP into the context window.",
249
+ };
250
+ }
251
+ }
252
+ catch {
253
+ // routing failure → allow passthrough
254
+ }
255
+ return undefined;
256
+ });
257
+ // ── 3. tool_result — post-tool-call event capture ─────
258
+ // OMP `tool_result` payload (refs/.../hooks/types.ts:461 onward) is
259
+ // `{toolName, toolCallId, input, content[], isError}`. We adapt to
260
+ // the Claude Code-shaped HookInput consumed by extractEvents.
261
+ pi.on("tool_result", (event) => {
262
+ try {
263
+ if (!_sessionId)
264
+ return undefined;
265
+ const rawToolName = String(event?.toolName ?? "");
266
+ const mappedToolName = OMP_TOOL_MAP[rawToolName.toLowerCase()] ?? rawToolName;
267
+ const content = Array.isArray(event?.content) ? event.content : [];
268
+ const textParts = content
269
+ .filter((c) => c?.type === "text" && typeof c.text === "string")
270
+ .map((c) => c.text);
271
+ const resultStr = textParts.join("\n");
272
+ const hookInput = {
273
+ tool_name: mappedToolName,
274
+ tool_input: event?.input ?? {},
275
+ tool_response: resultStr,
276
+ tool_output: event?.isError ? { isError: true } : undefined,
277
+ };
278
+ const events = extractEvents(hookInput);
279
+ for (const ev of events) {
280
+ db.insertEvent(_sessionId, ev, "PostToolUse");
281
+ }
282
+ }
283
+ catch {
284
+ // best effort
285
+ }
286
+ return undefined;
287
+ });
288
+ // ── 4. session_before_compact — resume snapshot ───────
289
+ pi.on("session_before_compact", () => {
290
+ try {
291
+ if (!_sessionId)
292
+ return undefined;
293
+ const events = db.getEvents(_sessionId);
294
+ const snapshot = buildResumeSnapshot(events);
295
+ db.upsertResume(_sessionId, snapshot, events.length);
296
+ db.incrementCompactCount(_sessionId);
297
+ }
298
+ catch {
299
+ // best effort
300
+ }
301
+ return undefined;
302
+ });
303
+ // ── 5. turn_end — per-turn token + provider cost capture ──
304
+ // OMP exposes REAL per-turn tokens AND a provider-computed USD cost on the
305
+ // completed turn's AssistantMessage (`event.message.usage` / `.model`),
306
+ // delivered INCREMENTALLY per turn (matrix §2,§5). parseOmpUsage maps that to
307
+ // the buildAgentUsageEvent counts (Usage.cacheWrite→cache_creation,
308
+ // cacheRead→cache_read, cost.total→native_cost_usd). buildAgentUsageEvent
309
+ // prefers the native cost over the local price table and returns null on an
310
+ // all-zero turn. We persist via db.insertEvent — the SessionDB-backed forward
311
+ // path used everywhere in this in-process plugin runtime (the .mjs
312
+ // attributeAndInsertEvents helper is the Claude-hook analogue, not reachable
313
+ // here). Best-effort: a usage parse must never break the turn.
314
+ pi.on("turn_end", (event) => {
315
+ try {
316
+ if (!_sessionId)
317
+ return undefined;
318
+ const counts = parseOmpUsage(event);
319
+ if (counts === null)
320
+ return undefined;
321
+ const usageEvent = buildAgentUsageEvent(counts);
322
+ if (usageEvent === null)
323
+ return undefined;
324
+ db.insertEvent(_sessionId, usageEvent, "PostToolUse");
325
+ }
326
+ catch {
327
+ // best effort — never break the turn on cost capture
328
+ }
329
+ return undefined;
330
+ });
331
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * adapters/omp/usage — pure parse of an OMP `turn_end` / `agent_end` payload
3
+ * into the {@link buildAgentUsageEvent} counts shape.
4
+ *
5
+ * Ground truth (docs/prds/2026-06-paid-observability/adapter-matrix/omp.md §2-4):
6
+ * - Per-turn usage rides on `AssistantMessage.usage`
7
+ * (`refs/platforms/omp/packages/ai/src/types.ts:505-541`).
8
+ * - The canonical `Usage` shape lives in pi-catalog
9
+ * (`refs/platforms/omp/packages/catalog/src/types.ts:100-145`): fields
10
+ * `input`, `output`, `cacheRead`, `cacheWrite`, `totalTokens`, plus a
11
+ * fully-resolved `cost: { input, output, cacheRead, cacheWrite, total }`
12
+ * (:138-144) — provider-computed, so NO client price table is needed.
13
+ * - `model: string` is `provider/model` on the `AssistantMessage` (:510).
14
+ * - `turn_end` carries `message: AssistantMessage`
15
+ * (`refs/.../extensibility/shared-events.ts:204-208`); `agent_end` carries
16
+ * `messages: AssistantMessage[]` (:191-194).
17
+ *
18
+ * Field mapping (matrix §3 → buildAgentUsageEvent input):
19
+ * Usage.input → input_tokens
20
+ * Usage.output → output_tokens
21
+ * Usage.cacheWrite → cache_creation_tokens (cache CREATION on the provider)
22
+ * Usage.cacheRead → cache_read_tokens
23
+ * message.model → model_id (the `provider/model` string)
24
+ * Usage.cost.total → native_cost_usd (provider-computed, preferred)
25
+ *
26
+ * Algorithmic + null-safe. NO regex. Returns `null` whenever there is no
27
+ * usable signal (no message / no usage / every token bucket absent-or-zero),
28
+ * so a malformed or empty turn emits nothing.
29
+ */
30
+ /**
31
+ * The counts object accepted by {@link buildAgentUsageEvent}. Re-declared here
32
+ * (structurally identical) so this module stays a leaf with no import cycle
33
+ * into the heavy `session/extract` module — the handler in `plugin.ts` passes
34
+ * the result straight through to `buildAgentUsageEvent`.
35
+ */
36
+ export interface OmpUsageCounts {
37
+ model_id: string;
38
+ input_tokens: number;
39
+ output_tokens: number;
40
+ cache_creation_tokens: number;
41
+ cache_read_tokens: number;
42
+ native_cost_usd: number | null;
43
+ }
44
+ /**
45
+ * Parse an OMP `turn_end` / `agent_end` event payload into the
46
+ * {@link buildAgentUsageEvent} counts shape, or `null` when no usable usage is
47
+ * present. Pure and side-effect free.
48
+ */
49
+ export declare function parseOmpUsage(payload: unknown): OmpUsageCounts | null;
@@ -0,0 +1,110 @@
1
+ /**
2
+ * adapters/omp/usage — pure parse of an OMP `turn_end` / `agent_end` payload
3
+ * into the {@link buildAgentUsageEvent} counts shape.
4
+ *
5
+ * Ground truth (docs/prds/2026-06-paid-observability/adapter-matrix/omp.md §2-4):
6
+ * - Per-turn usage rides on `AssistantMessage.usage`
7
+ * (`refs/platforms/omp/packages/ai/src/types.ts:505-541`).
8
+ * - The canonical `Usage` shape lives in pi-catalog
9
+ * (`refs/platforms/omp/packages/catalog/src/types.ts:100-145`): fields
10
+ * `input`, `output`, `cacheRead`, `cacheWrite`, `totalTokens`, plus a
11
+ * fully-resolved `cost: { input, output, cacheRead, cacheWrite, total }`
12
+ * (:138-144) — provider-computed, so NO client price table is needed.
13
+ * - `model: string` is `provider/model` on the `AssistantMessage` (:510).
14
+ * - `turn_end` carries `message: AssistantMessage`
15
+ * (`refs/.../extensibility/shared-events.ts:204-208`); `agent_end` carries
16
+ * `messages: AssistantMessage[]` (:191-194).
17
+ *
18
+ * Field mapping (matrix §3 → buildAgentUsageEvent input):
19
+ * Usage.input → input_tokens
20
+ * Usage.output → output_tokens
21
+ * Usage.cacheWrite → cache_creation_tokens (cache CREATION on the provider)
22
+ * Usage.cacheRead → cache_read_tokens
23
+ * message.model → model_id (the `provider/model` string)
24
+ * Usage.cost.total → native_cost_usd (provider-computed, preferred)
25
+ *
26
+ * Algorithmic + null-safe. NO regex. Returns `null` whenever there is no
27
+ * usable signal (no message / no usage / every token bucket absent-or-zero),
28
+ * so a malformed or empty turn emits nothing.
29
+ */
30
+ /** Coerce an unknown to a finite non-negative integer token count, else 0. */
31
+ function toTokenCount(value) {
32
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
33
+ return 0;
34
+ }
35
+ // Token counts are integers upstream; floor defensively against floats.
36
+ return Math.floor(value);
37
+ }
38
+ /** Coerce an unknown to a finite USD cost, else null (so the catalog can fill in). */
39
+ function toCostUsd(value) {
40
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
41
+ return null;
42
+ }
43
+ return value;
44
+ }
45
+ /** Narrow an unknown to a plain (non-null, non-array) object. */
46
+ function asRecord(value) {
47
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
48
+ return null;
49
+ }
50
+ return value;
51
+ }
52
+ /**
53
+ * Resolve the per-turn `AssistantMessage` from a `turn_end` (`.message`) or
54
+ * `agent_end` (`.messages[]`) payload. For `agent_end` we take the LAST
55
+ * assistant message — that is the turn that just completed and whose usage is
56
+ * the new delta (OMP usage is INCREMENTAL per turn, matrix §5).
57
+ */
58
+ function resolveMessage(payload) {
59
+ const single = asRecord(payload.message);
60
+ if (single !== null)
61
+ return single;
62
+ const list = payload.messages;
63
+ if (Array.isArray(list)) {
64
+ for (let i = list.length - 1; i >= 0; i--) {
65
+ const m = asRecord(list[i]);
66
+ if (m !== null)
67
+ return m;
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ /**
73
+ * Parse an OMP `turn_end` / `agent_end` event payload into the
74
+ * {@link buildAgentUsageEvent} counts shape, or `null` when no usable usage is
75
+ * present. Pure and side-effect free.
76
+ */
77
+ export function parseOmpUsage(payload) {
78
+ const root = asRecord(payload);
79
+ if (root === null)
80
+ return null;
81
+ const message = resolveMessage(root);
82
+ if (message === null)
83
+ return null;
84
+ const usage = asRecord(message.usage);
85
+ if (usage === null)
86
+ return null;
87
+ const input_tokens = toTokenCount(usage.input);
88
+ const output_tokens = toTokenCount(usage.output);
89
+ const cache_creation_tokens = toTokenCount(usage.cacheWrite);
90
+ const cache_read_tokens = toTokenCount(usage.cacheRead);
91
+ // No usable token signal → no event (mirrors buildAgentUsageEvent's own
92
+ // all-zero guard, but lets the handler short-circuit before building).
93
+ if (input_tokens <= 0 &&
94
+ output_tokens <= 0 &&
95
+ cache_creation_tokens <= 0 &&
96
+ cache_read_tokens <= 0) {
97
+ return null;
98
+ }
99
+ const model_id = typeof message.model === "string" ? message.model : "";
100
+ const cost = asRecord(usage.cost);
101
+ const native_cost_usd = cost !== null ? toCostUsd(cost.total) : null;
102
+ return {
103
+ model_id,
104
+ input_tokens,
105
+ output_tokens,
106
+ cache_creation_tokens,
107
+ cache_read_tokens,
108
+ native_cost_usd,
109
+ };
110
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * adapters/openclaw/config — Thin re-exports from OpenClawAdapter.
3
+ *
4
+ * This module exists for backward compatibility. All logic lives in the
5
+ * adapter class (index.ts). New code should use getAdapter() from detect.ts.
6
+ */
7
+ export { OpenClawAdapter } from "./index.js";
8
+ export { HOOK_EVENTS, LIFECYCLE_HOOKS, REQUIRED_HOOKS, OPTIONAL_HOOKS } from "./hooks.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * adapters/openclaw/config — Thin re-exports from OpenClawAdapter.
3
+ *
4
+ * This module exists for backward compatibility. All logic lives in the
5
+ * adapter class (index.ts). New code should use getAdapter() from detect.ts.
6
+ */
7
+ export { OpenClawAdapter } from "./index.js";
8
+ export { HOOK_EVENTS, LIFECYCLE_HOOKS, REQUIRED_HOOKS, OPTIONAL_HOOKS } from "./hooks.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * adapters/openclaw/hooks — OpenClaw hook definitions and validators.
3
+ *
4
+ * Defines the hook types and validation helpers specific to OpenClaw's
5
+ * TypeScript plugin paradigm. This module is used by:
6
+ * - CLI setup/upgrade commands (to configure plugin in openclaw.json)
7
+ * - Doctor command (to validate plugin configuration)
8
+ *
9
+ * OpenClaw hook system reference:
10
+ * - I/O: TS plugin functions via api.registerHook() and api.on()
11
+ * - Hook events: tool_call:before, tool_call:after, command:new, command:reset
12
+ * - Lifecycle: session_start, before_compaction, after_compaction, before_prompt_build, before_model_resolve
13
+ * - Context engine: api.registerContextEngine() with ownsCompaction
14
+ * - Blocking: return { block: true, blockReason } from tool_call:before
15
+ * - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
16
+ */
17
+ /** OpenClaw hook event names (registered via api.registerHook). */
18
+ export declare const HOOK_EVENTS: {
19
+ readonly TOOL_CALL_BEFORE: "tool_call:before";
20
+ readonly TOOL_CALL_AFTER: "tool_call:after";
21
+ readonly COMMAND_NEW: "command:new";
22
+ readonly COMMAND_RESET: "command:reset";
23
+ readonly COMMAND_STOP: "command:stop";
24
+ };
25
+ /** OpenClaw lifecycle hook names (registered via api.on). */
26
+ export declare const LIFECYCLE_HOOKS: {
27
+ readonly SESSION_START: "session_start";
28
+ readonly BEFORE_COMPACTION: "before_compaction";
29
+ readonly AFTER_COMPACTION: "after_compaction";
30
+ readonly BEFORE_PROMPT_BUILD: "before_prompt_build";
31
+ readonly BEFORE_MODEL_RESOLVE: "before_model_resolve";
32
+ readonly BEFORE_AGENT_START: "before_agent_start";
33
+ };
34
+ export type HookEvent = (typeof HOOK_EVENTS)[keyof typeof HOOK_EVENTS];
35
+ export type LifecycleHook = (typeof LIFECYCLE_HOOKS)[keyof typeof LIFECYCLE_HOOKS];
36
+ /**
37
+ * Required hooks that must be active for context-mode to function.
38
+ * OpenClaw registers these via api.registerHook() in the plugin entry point.
39
+ */
40
+ export declare const REQUIRED_HOOKS: HookEvent[];
41
+ /**
42
+ * Optional hooks that enhance functionality but aren't critical.
43
+ * command:new provides session cleanup; context engine handles compaction.
44
+ */
45
+ export declare const OPTIONAL_HOOKS: HookEvent[];
46
+ /**
47
+ * Check if a plugin entry is the context-mode plugin.
48
+ * OpenClaw plugins are registered by id in plugins.entries.
49
+ */
50
+ export declare function isContextModePlugin(pluginId: string): boolean;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * adapters/openclaw/hooks — OpenClaw hook definitions and validators.
3
+ *
4
+ * Defines the hook types and validation helpers specific to OpenClaw's
5
+ * TypeScript plugin paradigm. This module is used by:
6
+ * - CLI setup/upgrade commands (to configure plugin in openclaw.json)
7
+ * - Doctor command (to validate plugin configuration)
8
+ *
9
+ * OpenClaw hook system reference:
10
+ * - I/O: TS plugin functions via api.registerHook() and api.on()
11
+ * - Hook events: tool_call:before, tool_call:after, command:new, command:reset
12
+ * - Lifecycle: session_start, before_compaction, after_compaction, before_prompt_build, before_model_resolve
13
+ * - Context engine: api.registerContextEngine() with ownsCompaction
14
+ * - Blocking: return { block: true, blockReason } from tool_call:before
15
+ * - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
16
+ */
17
+ // ─────────────────────────────────────────────────────────
18
+ // Hook type constants
19
+ // ─────────────────────────────────────────────────────────
20
+ /** OpenClaw hook event names (registered via api.registerHook). */
21
+ export const HOOK_EVENTS = {
22
+ TOOL_CALL_BEFORE: "tool_call:before",
23
+ TOOL_CALL_AFTER: "tool_call:after",
24
+ COMMAND_NEW: "command:new",
25
+ COMMAND_RESET: "command:reset",
26
+ COMMAND_STOP: "command:stop",
27
+ };
28
+ /** OpenClaw lifecycle hook names (registered via api.on). */
29
+ export const LIFECYCLE_HOOKS = {
30
+ SESSION_START: "session_start",
31
+ BEFORE_COMPACTION: "before_compaction",
32
+ AFTER_COMPACTION: "after_compaction",
33
+ BEFORE_PROMPT_BUILD: "before_prompt_build",
34
+ BEFORE_MODEL_RESOLVE: "before_model_resolve",
35
+ BEFORE_AGENT_START: "before_agent_start",
36
+ };
37
+ // ─────────────────────────────────────────────────────────
38
+ // Hook validation
39
+ // ─────────────────────────────────────────────────────────
40
+ /**
41
+ * Required hooks that must be active for context-mode to function.
42
+ * OpenClaw registers these via api.registerHook() in the plugin entry point.
43
+ */
44
+ export const REQUIRED_HOOKS = [
45
+ HOOK_EVENTS.TOOL_CALL_BEFORE,
46
+ HOOK_EVENTS.TOOL_CALL_AFTER,
47
+ ];
48
+ /**
49
+ * Optional hooks that enhance functionality but aren't critical.
50
+ * command:new provides session cleanup; context engine handles compaction.
51
+ */
52
+ export const OPTIONAL_HOOKS = [
53
+ HOOK_EVENTS.COMMAND_NEW,
54
+ ];
55
+ /**
56
+ * Check if a plugin entry is the context-mode plugin.
57
+ * OpenClaw plugins are registered by id in plugins.entries.
58
+ */
59
+ export function isContextModePlugin(pluginId) {
60
+ return pluginId.includes("context-mode");
61
+ }