@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.
- package/.claude-plugin/marketplace.json +33 -0
- package/.claude-plugin/plugin.json +31 -0
- package/.codex-plugin/hooks.json +65 -0
- package/.codex-plugin/mcp.json +13 -0
- package/.codex-plugin/plugin.json +31 -0
- package/.openclaw-plugin/index.ts +12 -0
- package/.openclaw-plugin/openclaw.plugin.json +38 -0
- package/.openclaw-plugin/package.json +28 -0
- package/LICENSE +94 -0
- package/README.md +1619 -0
- package/bin/statusline.mjs +378 -0
- package/build/adapters/antigravity/index.d.ts +51 -0
- package/build/adapters/antigravity/index.js +169 -0
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +342 -0
- package/build/adapters/base.d.ts +92 -0
- package/build/adapters/base.js +135 -0
- package/build/adapters/claude-code/config.d.ts +8 -0
- package/build/adapters/claude-code/config.js +8 -0
- package/build/adapters/claude-code/hooks.d.ts +106 -0
- package/build/adapters/claude-code/hooks.js +183 -0
- package/build/adapters/claude-code/index.d.ts +80 -0
- package/build/adapters/claude-code/index.js +569 -0
- package/build/adapters/claude-code-base.d.ts +49 -0
- package/build/adapters/claude-code-base.js +113 -0
- package/build/adapters/client-map.d.ts +10 -0
- package/build/adapters/client-map.js +42 -0
- package/build/adapters/codex/config.d.ts +8 -0
- package/build/adapters/codex/config.js +8 -0
- package/build/adapters/codex/hooks.d.ts +55 -0
- package/build/adapters/codex/hooks.js +64 -0
- package/build/adapters/codex/index.d.ts +87 -0
- package/build/adapters/codex/index.js +995 -0
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/codex/usage.d.ts +107 -0
- package/build/adapters/codex/usage.js +227 -0
- package/build/adapters/copilot-base.d.ts +94 -0
- package/build/adapters/copilot-base.js +297 -0
- package/build/adapters/copilot-cli/hooks.d.ts +33 -0
- package/build/adapters/copilot-cli/hooks.js +64 -0
- package/build/adapters/copilot-cli/index.d.ts +48 -0
- package/build/adapters/copilot-cli/index.js +341 -0
- package/build/adapters/cursor/config.d.ts +4 -0
- package/build/adapters/cursor/config.js +4 -0
- package/build/adapters/cursor/hooks.d.ts +55 -0
- package/build/adapters/cursor/hooks.js +82 -0
- package/build/adapters/cursor/index.d.ts +64 -0
- package/build/adapters/cursor/index.js +517 -0
- package/build/adapters/detect.d.ts +136 -0
- package/build/adapters/detect.js +623 -0
- package/build/adapters/gemini-cli/config.d.ts +8 -0
- package/build/adapters/gemini-cli/config.js +8 -0
- package/build/adapters/gemini-cli/hooks.d.ts +83 -0
- package/build/adapters/gemini-cli/hooks.js +115 -0
- package/build/adapters/gemini-cli/index.d.ts +80 -0
- package/build/adapters/gemini-cli/index.js +483 -0
- package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
- package/build/adapters/jetbrains-copilot/config.js +8 -0
- package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
- package/build/adapters/jetbrains-copilot/hooks.js +87 -0
- package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
- package/build/adapters/jetbrains-copilot/index.js +131 -0
- package/build/adapters/kimi/config.d.ts +8 -0
- package/build/adapters/kimi/config.js +8 -0
- package/build/adapters/kimi/hooks.d.ts +28 -0
- package/build/adapters/kimi/hooks.js +34 -0
- package/build/adapters/kimi/index.d.ts +66 -0
- package/build/adapters/kimi/index.js +537 -0
- package/build/adapters/kimi/paths.d.ts +21 -0
- package/build/adapters/kimi/paths.js +52 -0
- package/build/adapters/kimi/usage.d.ts +82 -0
- package/build/adapters/kimi/usage.js +217 -0
- package/build/adapters/kiro/hooks.d.ts +61 -0
- package/build/adapters/kiro/hooks.js +94 -0
- package/build/adapters/kiro/index.d.ts +61 -0
- package/build/adapters/kiro/index.js +301 -0
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +81 -0
- package/build/adapters/omp/plugin.js +331 -0
- package/build/adapters/omp/usage.d.ts +49 -0
- package/build/adapters/omp/usage.js +110 -0
- package/build/adapters/openclaw/config.d.ts +8 -0
- package/build/adapters/openclaw/config.js +8 -0
- package/build/adapters/openclaw/hooks.d.ts +50 -0
- package/build/adapters/openclaw/hooks.js +61 -0
- package/build/adapters/openclaw/index.d.ts +74 -0
- package/build/adapters/openclaw/index.js +439 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +207 -0
- package/build/adapters/openclaw/plugin.d.ts +141 -0
- package/build/adapters/openclaw/plugin.js +818 -0
- package/build/adapters/openclaw/session-db.d.ts +55 -0
- package/build/adapters/openclaw/session-db.js +88 -0
- package/build/adapters/openclaw/usage.d.ts +34 -0
- package/build/adapters/openclaw/usage.js +52 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/config.d.ts +8 -0
- package/build/adapters/opencode/config.js +8 -0
- package/build/adapters/opencode/hooks.d.ts +38 -0
- package/build/adapters/opencode/hooks.js +50 -0
- package/build/adapters/opencode/index.d.ts +144 -0
- package/build/adapters/opencode/index.js +638 -0
- package/build/adapters/opencode/plugin.d.ts +249 -0
- package/build/adapters/opencode/plugin.js +1536 -0
- package/build/adapters/opencode/v2.d.ts +117 -0
- package/build/adapters/opencode/v2.js +224 -0
- package/build/adapters/opencode/zod3tov4.d.ts +1 -0
- package/build/adapters/opencode/zod3tov4.js +132 -0
- package/build/adapters/pi/extension.d.ts +81 -0
- package/build/adapters/pi/extension.js +861 -0
- package/build/adapters/pi/index.d.ts +57 -0
- package/build/adapters/pi/index.js +173 -0
- package/build/adapters/pi/mcp-bridge.d.ts +268 -0
- package/build/adapters/pi/mcp-bridge.js +894 -0
- package/build/adapters/qwen-code/hooks.d.ts +26 -0
- package/build/adapters/qwen-code/hooks.js +29 -0
- package/build/adapters/qwen-code/index.d.ts +40 -0
- package/build/adapters/qwen-code/index.js +361 -0
- package/build/adapters/qwen-code/usage.d.ts +90 -0
- package/build/adapters/qwen-code/usage.js +222 -0
- package/build/adapters/types.d.ts +360 -0
- package/build/adapters/types.js +118 -0
- package/build/adapters/vscode-copilot/config.d.ts +8 -0
- package/build/adapters/vscode-copilot/config.js +8 -0
- package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
- package/build/adapters/vscode-copilot/hooks.js +98 -0
- package/build/adapters/vscode-copilot/index.d.ts +32 -0
- package/build/adapters/vscode-copilot/index.js +222 -0
- package/build/adapters/zed/index.d.ts +40 -0
- package/build/adapters/zed/index.js +163 -0
- package/build/cli.d.ts +34 -0
- package/build/cli.js +1828 -0
- package/build/db-base.d.ts +250 -0
- package/build/db-base.js +756 -0
- package/build/executor.d.ts +72 -0
- package/build/executor.js +678 -0
- package/build/exit-classify.d.ts +19 -0
- package/build/exit-classify.js +12 -0
- package/build/fetch/blocks.d.ts +145 -0
- package/build/fetch/blocks.js +244 -0
- package/build/fetch/extract.d.ts +68 -0
- package/build/fetch/extract.js +123 -0
- package/build/fetch/page-store.d.ts +66 -0
- package/build/fetch/page-store.js +174 -0
- package/build/fetch-cache.d.ts +13 -0
- package/build/fetch-cache.js +15 -0
- package/build/lifecycle.d.ts +124 -0
- package/build/lifecycle.js +278 -0
- package/build/package-identity.d.ts +26 -0
- package/build/package-identity.js +28 -0
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.d.ts +71 -0
- package/build/runtime.js +659 -0
- package/build/search/auto-memory.d.ts +47 -0
- package/build/search/auto-memory.js +170 -0
- package/build/search/ctx-search-schema.d.ts +90 -0
- package/build/search/ctx-search-schema.js +135 -0
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/search/unified.d.ts +56 -0
- package/build/search/unified.js +104 -0
- package/build/security.d.ts +206 -0
- package/build/security.js +780 -0
- package/build/server.d.ts +240 -0
- package/build/server.js +5034 -0
- package/build/session/analytics.d.ts +773 -0
- package/build/session/analytics.js +2420 -0
- package/build/session/db.d.ts +468 -0
- package/build/session/db.js +1330 -0
- package/build/session/error-classifier.d.ts +87 -0
- package/build/session/error-classifier.js +303 -0
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +333 -0
- package/build/session/extract.js +2688 -0
- package/build/session/model-prices.json +429 -0
- package/build/session/persist-tool-calls.d.ts +54 -0
- package/build/session/persist-tool-calls.js +105 -0
- package/build/session/pricing.d.ts +64 -0
- package/build/session/pricing.js +151 -0
- package/build/session/project-attribution.d.ts +73 -0
- package/build/session/project-attribution.js +245 -0
- package/build/session/purge.d.ts +138 -0
- package/build/session/purge.js +240 -0
- package/build/session/retrieval-marker.d.ts +39 -0
- package/build/session/retrieval-marker.js +65 -0
- package/build/session/snapshot.d.ts +46 -0
- package/build/session/snapshot.js +532 -0
- package/build/store-directory.d.ts +56 -0
- package/build/store-directory.js +254 -0
- package/build/store.d.ts +147 -0
- package/build/store.js +1714 -0
- package/build/truncate.d.ts +57 -0
- package/build/truncate.js +142 -0
- package/build/types.d.ts +117 -0
- package/build/types.js +20 -0
- package/build/util/claude-config.d.ts +32 -0
- package/build/util/claude-config.js +84 -0
- package/build/util/hook-config.d.ts +27 -0
- package/build/util/hook-config.js +76 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/build/util/plugin-cache-integrity.d.ts +51 -0
- package/build/util/plugin-cache-integrity.js +146 -0
- package/build/util/project-dir.d.ts +149 -0
- package/build/util/project-dir.js +340 -0
- package/build/util/sibling-mcp.d.ts +79 -0
- package/build/util/sibling-mcp.js +181 -0
- package/cli.bundle.mjs +1495 -0
- package/configs/antigravity/GEMINI.md +68 -0
- package/configs/antigravity/mcp_config.json +7 -0
- package/configs/antigravity-cli/hooks/hooks.json +37 -0
- package/configs/antigravity-cli/hooks.json +37 -0
- package/configs/antigravity-cli/mcp_config.json +10 -0
- package/configs/antigravity-cli/plugin.json +14 -0
- package/configs/antigravity-cli/rules/context-mode.md +77 -0
- package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
- package/configs/claude-code/CLAUDE.md +91 -0
- package/configs/codex/AGENTS.md +99 -0
- package/configs/codex/config.toml +8 -0
- package/configs/codex/hooks.json +47 -0
- package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
- package/configs/copilot-cli/.mcp.json +12 -0
- package/configs/copilot-cli/README.md +47 -0
- package/configs/copilot-cli/hooks.json +41 -0
- package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
- package/configs/cursor/context-mode.mdc +76 -0
- package/configs/cursor/hooks.json +21 -0
- package/configs/cursor/mcp.json +7 -0
- package/configs/gemini-cli/GEMINI.md +88 -0
- package/configs/gemini-cli/mcp.json +7 -0
- package/configs/gemini-cli/settings.json +60 -0
- package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
- package/configs/jetbrains-copilot/hooks.json +16 -0
- package/configs/jetbrains-copilot/mcp.json +7 -0
- package/configs/kilo/AGENTS.md +84 -0
- package/configs/kilo/kilo.json +6 -0
- package/configs/kimi/hooks.json +54 -0
- package/configs/kiro/KIRO.md +84 -0
- package/configs/kiro/agent.json +18 -0
- package/configs/kiro/mcp.json +7 -0
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +87 -0
- package/configs/openclaw/openclaw.json +13 -0
- package/configs/opencode/AGENTS.md +84 -0
- package/configs/opencode/opencode.json +6 -0
- package/configs/pi/AGENTS.md +3 -0
- package/configs/qwen-code/QWEN.md +91 -0
- package/configs/vscode-copilot/copilot-instructions.md +80 -0
- package/configs/vscode-copilot/hooks.json +16 -0
- package/configs/vscode-copilot/mcp.json +7 -0
- package/configs/zed/AGENTS.md +68 -0
- package/hooks/antigravity-cli/payload.mjs +98 -0
- package/hooks/antigravity-cli/posttooluse.mjs +138 -0
- package/hooks/antigravity-cli/pretooluse.mjs +78 -0
- package/hooks/antigravity-cli/stop.mjs +58 -0
- package/hooks/auto-injection.mjs +102 -0
- package/hooks/cache-heal-utils.mjs +379 -0
- package/hooks/codex/platform.mjs +1 -0
- package/hooks/codex/posttooluse.mjs +66 -0
- package/hooks/codex/precompact.mjs +70 -0
- package/hooks/codex/pretooluse.mjs +42 -0
- package/hooks/codex/sessionstart.mjs +121 -0
- package/hooks/codex/stop.mjs +138 -0
- package/hooks/codex/userpromptsubmit.mjs +75 -0
- package/hooks/copilot-cli/posttooluse.mjs +79 -0
- package/hooks/copilot-cli/precompact.mjs +66 -0
- package/hooks/copilot-cli/pretooluse.mjs +41 -0
- package/hooks/copilot-cli/sessionstart.mjs +121 -0
- package/hooks/copilot-cli/stop.mjs +59 -0
- package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
- package/hooks/core/codex-caps.mjs +112 -0
- package/hooks/core/formatters.mjs +356 -0
- package/hooks/core/mcp-ready.mjs +109 -0
- package/hooks/core/platform-detect.mjs +49 -0
- package/hooks/core/routing.mjs +1050 -0
- package/hooks/core/stdin.mjs +90 -0
- package/hooks/core/tool-naming.mjs +57 -0
- package/hooks/cursor/afteragentresponse.mjs +74 -0
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +72 -0
- package/hooks/cursor/pretooluse.mjs +26 -0
- package/hooks/cursor/sessionstart.mjs +97 -0
- package/hooks/cursor/stop.mjs +48 -0
- package/hooks/ensure-deps.mjs +254 -0
- package/hooks/formatters/claude-code.mjs +113 -0
- package/hooks/formatters/cursor.mjs +37 -0
- package/hooks/formatters/gemini-cli.mjs +55 -0
- package/hooks/formatters/vscode-copilot.mjs +55 -0
- package/hooks/gemini-cli/aftermodel.mjs +70 -0
- package/hooks/gemini-cli/aftertool.mjs +61 -0
- package/hooks/gemini-cli/beforeagent.mjs +99 -0
- package/hooks/gemini-cli/beforetool.mjs +27 -0
- package/hooks/gemini-cli/precompress.mjs +55 -0
- package/hooks/gemini-cli/sessionstart.mjs +137 -0
- package/hooks/heal-partial-install.mjs +712 -0
- package/hooks/hooks.json +143 -0
- package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
- package/hooks/jetbrains-copilot/precompact.mjs +55 -0
- package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
- package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
- package/hooks/kimi/platform.mjs +1 -0
- package/hooks/kimi/posttooluse.mjs +72 -0
- package/hooks/kimi/precompact.mjs +80 -0
- package/hooks/kimi/pretooluse.mjs +42 -0
- package/hooks/kimi/sessionend.mjs +61 -0
- package/hooks/kimi/sessionstart.mjs +113 -0
- package/hooks/kimi/stop.mjs +132 -0
- package/hooks/kimi/userpromptsubmit.mjs +90 -0
- package/hooks/kiro/agentspawn.mjs +97 -0
- package/hooks/kiro/posttooluse.mjs +51 -0
- package/hooks/kiro/pretooluse.mjs +64 -0
- package/hooks/kiro/userpromptsubmit.mjs +88 -0
- package/hooks/normalize-hooks.mjs +323 -0
- package/hooks/platform-bridge.mjs +340 -0
- package/hooks/posttooluse.mjs +225 -0
- package/hooks/precompact.mjs +100 -0
- package/hooks/pretooluse.mjs +227 -0
- package/hooks/qwen-code/platform.mjs +1 -0
- package/hooks/qwen-code/stop.mjs +168 -0
- package/hooks/routing-block.mjs +105 -0
- package/hooks/run-hook.mjs +95 -0
- package/hooks/security.bundle.mjs +2 -0
- package/hooks/session-attribution.bundle.mjs +1 -0
- package/hooks/session-db.bundle.mjs +145 -0
- package/hooks/session-directive.mjs +520 -0
- package/hooks/session-extract.bundle.mjs +3 -0
- package/hooks/session-helpers.mjs +428 -0
- package/hooks/session-loaders.mjs +448 -0
- package/hooks/session-snapshot.bundle.mjs +32 -0
- package/hooks/sessionstart.mjs +464 -0
- package/hooks/stop.mjs +82 -0
- package/hooks/suppress-stderr.mjs +25 -0
- package/hooks/userpromptsubmit.mjs +108 -0
- package/hooks/vscode-copilot/posttooluse.mjs +61 -0
- package/hooks/vscode-copilot/precompact.mjs +55 -0
- package/hooks/vscode-copilot/pretooluse.mjs +27 -0
- package/hooks/vscode-copilot/sessionstart.mjs +129 -0
- package/openclaw.plugin.json +38 -0
- package/package.json +132 -0
- package/scripts/heal-better-sqlite3.mjs +430 -0
- package/scripts/heal-installed-plugins.mjs +635 -0
- package/scripts/plugin-cache-integrity.mjs +248 -0
- package/scripts/postinstall.mjs +397 -0
- package/server.bundle.mjs +1421 -0
- package/server.js +5 -0
- package/skills/.ignore +7 -0
- package/skills/context-mode/SKILL.md +300 -0
- package/skills/context-mode/references/anti-patterns.md +283 -0
- package/skills/context-mode/references/patterns-javascript.md +298 -0
- package/skills/context-mode/references/patterns-python.md +304 -0
- package/skills/context-mode/references/patterns-shell.md +277 -0
- package/skills/ctx-doctor/SKILL.md +22 -0
- package/skills/ctx-index/SKILL.md +46 -0
- package/skills/ctx-insight/SKILL.md +24 -0
- package/skills/ctx-purge/SKILL.md +49 -0
- package/skills/ctx-search/SKILL.md +35 -0
- package/skills/ctx-stats/SKILL.md +26 -0
- package/skills/ctx-upgrade/SKILL.md +31 -0
- 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
|
+
}
|