@axonflow/openclaw 0.1.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.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * before_tool_call hook — input governance.
3
+ *
4
+ * Evaluates tool arguments against AxonFlow policies before execution.
5
+ * Can block the call, require human approval, or allow through.
6
+ */
7
+ import { shouldGovernTool } from "./config.js";
8
+ /** Derive connector_type from tool name for AxonFlow policy evaluation. */
9
+ export function deriveConnectorType(toolName) {
10
+ return `openclaw.${toolName}`;
11
+ }
12
+ /**
13
+ * Create the before_tool_call hook handler.
14
+ *
15
+ * Decision logic:
16
+ * 1. If tool is excluded from governance: allow through (no check)
17
+ * 2. Call mcp_check_input with tool args serialized as JSON
18
+ * 3. If blocked by policy: return { block: true, blockReason }
19
+ * 4. If tool is in highRiskTools AND allowed: return { requireApproval }
20
+ * 5. Otherwise: allow through
21
+ */
22
+ export function createBeforeToolCallHandler(client, config) {
23
+ return async (event) => {
24
+ if (!shouldGovernTool(event.toolName, config)) {
25
+ return undefined;
26
+ }
27
+ const connectorType = deriveConnectorType(event.toolName);
28
+ const statement = JSON.stringify(event.params);
29
+ let check;
30
+ try {
31
+ check = await client.mcpCheckInput(connectorType, statement, config.defaultOperation ?? "execute");
32
+ }
33
+ catch (err) {
34
+ if (config.onError === "allow") {
35
+ return undefined; // Fail-open: allow tool execution
36
+ }
37
+ return {
38
+ block: true,
39
+ blockReason: `AxonFlow unreachable: ${err instanceof Error ? err.message : "unknown error"}`,
40
+ };
41
+ }
42
+ if (!check.allowed) {
43
+ return {
44
+ block: true,
45
+ blockReason: check.block_reason ?? "Blocked by AxonFlow policy",
46
+ };
47
+ }
48
+ // High-risk tools get approval even when policy allows
49
+ if (config.highRiskTools &&
50
+ config.highRiskTools.includes(event.toolName)) {
51
+ return {
52
+ requireApproval: {
53
+ title: `AxonFlow: ${event.toolName} requires approval`,
54
+ description: `Tool call governed by AxonFlow. ${check.policies_evaluated} policies evaluated.`,
55
+ severity: "warning",
56
+ timeoutMs: 60_000,
57
+ timeoutBehavior: "deny",
58
+ },
59
+ };
60
+ }
61
+ return undefined;
62
+ };
63
+ }
64
+ //# sourceMappingURL=governance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"governance.js","sourceRoot":"","sources":["../src/governance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAgB/C,2EAA2E;AAC3E,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,OAAO,YAAY,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,2BAA2B,CACzC,MAAsB,EACtB,MAA4B;IAE5B,OAAO,KAAK,EAAE,KAKb,EAA6C,EAAE;QAC9C,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;YAC9C,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,aAAa,GAAG,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE/C,IAAI,KAAK,CAAC;QACV,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,MAAM,CAAC,aAAa,CAChC,aAAa,EACb,SAAS,EACT,MAAM,CAAC,gBAAgB,IAAI,SAAS,CACrC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;gBAC/B,OAAO,SAAS,CAAC,CAAC,kCAAkC;YACtD,CAAC;YACD,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,WAAW,EAAE,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE;aAC7F,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,WAAW,EAAE,KAAK,CAAC,YAAY,IAAI,4BAA4B;aAChE,CAAC;QACJ,CAAC;QAED,uDAAuD;QACvD,IACE,MAAM,CAAC,aAAa;YACpB,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAC7C,CAAC;YACD,OAAO;gBACL,eAAe,EAAE;oBACf,KAAK,EAAE,aAAa,KAAK,CAAC,QAAQ,oBAAoB;oBACtD,WAAW,EAAE,mCAAmC,KAAK,CAAC,kBAAkB,sBAAsB;oBAC9F,QAAQ,EAAE,SAAS;oBACnB,SAAS,EAAE,MAAM;oBACjB,eAAe,EAAE,MAAM;iBACxB;aACF,CAAC;QACJ,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * AxonFlow Governance Plugin for OpenClaw
3
+ *
4
+ * Adds centralized policy enforcement, PII detection, and audit trails
5
+ * to OpenClaw tool execution. Works with all OpenClaw tools: built-in,
6
+ * plugin-provided, and MCP-backed.
7
+ *
8
+ * Install:
9
+ * openclaw plugins install @axonflow/openclaw
10
+ *
11
+ * Configure in your OpenClaw config:
12
+ * plugins:
13
+ * @axonflow/openclaw:
14
+ * endpoint: http://localhost:8080
15
+ * clientId: your-client-id
16
+ * clientSecret: your-secret
17
+ * highRiskTools:
18
+ * - web_fetch
19
+ * - message
20
+ *
21
+ * What this plugin does (5 hooks):
22
+ * 1. before_tool_call: evaluates tool arguments against AxonFlow policies
23
+ * 2. after_tool_call: logs tool execution to AxonFlow's audit trail
24
+ * 3. message_sending: scans outbound messages, cancels or redacts PII
25
+ * 4. llm_input: records prompt, model, provider to audit trail
26
+ * 5. llm_output: records response, token usage, latency to audit trail
27
+ *
28
+ * Note: tool_result_persist (output scanning) is not registered because
29
+ * OpenClaw's hook is sync-only and cannot make async HTTP calls to AxonFlow.
30
+ * Outbound messages ARE scanned via message_sending. See upstream issue
31
+ * for async hook support.
32
+ */
33
+ export { AxonFlowClient } from "./axonflow-client.js";
34
+ export type { AxonFlowPluginConfig } from "./config.js";
35
+ export { resolveConfig, shouldGovernTool } from "./config.js";
36
+ export { deriveConnectorType } from "./governance.js";
37
+ /**
38
+ * Plugin registration function.
39
+ *
40
+ * Called by OpenClaw when the plugin is loaded. Reads configuration,
41
+ * creates the AxonFlow client, and registers five governance/audit hooks.
42
+ *
43
+ * Compatible with OpenClaw's `definePluginEntry` or direct registration:
44
+ *
45
+ * // With definePluginEntry:
46
+ * export default definePluginEntry({
47
+ * id: "axonflow-governance",
48
+ * register: registerAxonFlowGovernance,
49
+ * });
50
+ *
51
+ * // Or direct:
52
+ * api.registerHook("before_tool_call", handler);
53
+ */
54
+ export declare function registerAxonFlowGovernance(api: {
55
+ pluginConfig?: Record<string, unknown>;
56
+ logger: {
57
+ info: (msg: string) => void;
58
+ error: (msg: string) => void;
59
+ };
60
+ on: (hookName: string, handler: (...args: any[]) => any, opts?: {
61
+ priority?: number;
62
+ }) => void;
63
+ }): void;
64
+ /**
65
+ * Default export for OpenClaw plugin loader.
66
+ *
67
+ * OpenClaw expects extensions to export a default object with `id`, `name`,
68
+ * and `register` function. This is the entry point when installed via
69
+ * `openclaw plugins install @axonflow/openclaw`.
70
+ */
71
+ declare const _default: {
72
+ id: string;
73
+ name: string;
74
+ description: string;
75
+ register: typeof registerAxonFlowGovernance;
76
+ };
77
+ export default _default;
78
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAUH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACtE,EAAE,EAAE,CACF,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAChC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KACzB,IAAI,CAAC;CACX,GAAG,IAAI,CA4BP;AAED;;;;;;GAMG;;;;;;;AACH,wBAKE"}
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ /**
2
+ * AxonFlow Governance Plugin for OpenClaw
3
+ *
4
+ * Adds centralized policy enforcement, PII detection, and audit trails
5
+ * to OpenClaw tool execution. Works with all OpenClaw tools: built-in,
6
+ * plugin-provided, and MCP-backed.
7
+ *
8
+ * Install:
9
+ * openclaw plugins install @axonflow/openclaw
10
+ *
11
+ * Configure in your OpenClaw config:
12
+ * plugins:
13
+ * @axonflow/openclaw:
14
+ * endpoint: http://localhost:8080
15
+ * clientId: your-client-id
16
+ * clientSecret: your-secret
17
+ * highRiskTools:
18
+ * - web_fetch
19
+ * - message
20
+ *
21
+ * What this plugin does (5 hooks):
22
+ * 1. before_tool_call: evaluates tool arguments against AxonFlow policies
23
+ * 2. after_tool_call: logs tool execution to AxonFlow's audit trail
24
+ * 3. message_sending: scans outbound messages, cancels or redacts PII
25
+ * 4. llm_input: records prompt, model, provider to audit trail
26
+ * 5. llm_output: records response, token usage, latency to audit trail
27
+ *
28
+ * Note: tool_result_persist (output scanning) is not registered because
29
+ * OpenClaw's hook is sync-only and cannot make async HTTP calls to AxonFlow.
30
+ * Outbound messages ARE scanned via message_sending. See upstream issue
31
+ * for async hook support.
32
+ */
33
+ import { AxonFlowClient } from "./axonflow-client.js";
34
+ import { resolveConfig } from "./config.js";
35
+ import { createBeforeToolCallHandler } from "./governance.js";
36
+ import { createAfterToolCallHandler } from "./audit.js";
37
+ import { createMessageSendingHandler } from "./message-guard.js";
38
+ import { createLlmInputHandler, createLlmOutputHandler } from "./llm-audit.js";
39
+ // Re-export for external consumers
40
+ export { AxonFlowClient } from "./axonflow-client.js";
41
+ export { resolveConfig, shouldGovernTool } from "./config.js";
42
+ export { deriveConnectorType } from "./governance.js";
43
+ /**
44
+ * Plugin registration function.
45
+ *
46
+ * Called by OpenClaw when the plugin is loaded. Reads configuration,
47
+ * creates the AxonFlow client, and registers five governance/audit hooks.
48
+ *
49
+ * Compatible with OpenClaw's `definePluginEntry` or direct registration:
50
+ *
51
+ * // With definePluginEntry:
52
+ * export default definePluginEntry({
53
+ * id: "axonflow-governance",
54
+ * register: registerAxonFlowGovernance,
55
+ * });
56
+ *
57
+ * // Or direct:
58
+ * api.registerHook("before_tool_call", handler);
59
+ */
60
+ export function registerAxonFlowGovernance(api) {
61
+ const config = resolveConfig(api.pluginConfig);
62
+ const client = new AxonFlowClient(config);
63
+ api.logger.info(`AxonFlow governance active: endpoint=${config.endpoint}, ` +
64
+ `highRiskTools=[${(config.highRiskTools ?? []).join(",")}]`);
65
+ // Hook 1: Input governance (before tool execution)
66
+ const beforeToolCall = createBeforeToolCallHandler(client, config);
67
+ api.on("before_tool_call", beforeToolCall, { priority: 10 });
68
+ // Hook 2: Audit logging (after tool execution)
69
+ const afterToolCall = createAfterToolCallHandler(client, config);
70
+ api.on("after_tool_call", afterToolCall, { priority: 90 });
71
+ // Hook 3: Outbound message governance (before message reaches user)
72
+ const messageSending = createMessageSendingHandler(client, config);
73
+ api.on("message_sending", messageSending, { priority: 10 });
74
+ // Hook 4-5: LLM call audit (observe-only, cannot block/modify)
75
+ const llmCallState = new Map();
76
+ const llmInput = createLlmInputHandler(client, config, llmCallState);
77
+ api.on("llm_input", llmInput, { priority: 90 });
78
+ const llmOutput = createLlmOutputHandler(client, config, llmCallState);
79
+ api.on("llm_output", llmOutput, { priority: 90 });
80
+ }
81
+ /**
82
+ * Default export for OpenClaw plugin loader.
83
+ *
84
+ * OpenClaw expects extensions to export a default object with `id`, `name`,
85
+ * and `register` function. This is the entry point when installed via
86
+ * `openclaw plugins install @axonflow/openclaw`.
87
+ */
88
+ export default {
89
+ id: "axonflow-governance",
90
+ name: "AxonFlow Governance",
91
+ description: "Policy enforcement for tool inputs, PII scanning on outbound messages, and audit trails for OpenClaw",
92
+ register: registerAxonFlowGovernance,
93
+ };
94
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE/E,mCAAmC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAQ1C;IACC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IAE1C,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,wCAAwC,MAAM,CAAC,QAAQ,IAAI;QACzD,kBAAkB,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAC9D,CAAC;IAEF,mDAAmD;IACnD,MAAM,cAAc,GAAG,2BAA2B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnE,GAAG,CAAC,EAAE,CAAC,kBAAkB,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAE7D,+CAA+C;IAC/C,MAAM,aAAa,GAAG,0BAA0B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjE,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,aAAa,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAE3D,oEAAoE;IACpE,MAAM,cAAc,GAAG,2BAA2B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnE,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAE5D,+DAA+D;IAC/D,MAAM,YAAY,GAAG,IAAI,GAAG,EAAgF,CAAC;IAC7G,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IACrE,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAEhD,MAAM,SAAS,GAAG,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IACvE,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;GAMG;AACH,eAAe;IACb,EAAE,EAAE,qBAAqB;IACzB,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,sGAAsG;IACnH,QAAQ,EAAE,0BAA0B;CACrC,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * llm_input and llm_output hooks — LLM call audit logging.
3
+ *
4
+ * Records what the LLM sees (prompt, model, provider) and produces
5
+ * (response, token usage) to AxonFlow's audit trail. These hooks are
6
+ * observe-only (cannot block or modify), so they provide audit
7
+ * evidence, not governance.
8
+ */
9
+ import type { AxonFlowClient } from "./axonflow-client.js";
10
+ import type { AxonFlowPluginConfig } from "./config.js";
11
+ /** Shared state for correlating llm_input with llm_output. */
12
+ interface LLMCallState {
13
+ provider: string;
14
+ model: string;
15
+ prompt: string;
16
+ startMs: number;
17
+ }
18
+ /**
19
+ * Create the llm_input hook handler.
20
+ *
21
+ * Records the prompt, model, and provider at the start of each LLM call.
22
+ * Stores state by runId for correlation with the llm_output handler.
23
+ */
24
+ export declare function createLlmInputHandler(_client: AxonFlowClient, _config: AxonFlowPluginConfig, callState: Map<string, LLMCallState>): (event: {
25
+ runId: string;
26
+ sessionId: string;
27
+ provider: string;
28
+ model: string;
29
+ systemPrompt?: string;
30
+ prompt: string;
31
+ historyMessages: unknown[];
32
+ imagesCount: number;
33
+ }) => void;
34
+ /**
35
+ * Create the llm_output hook handler.
36
+ *
37
+ * Correlates with the stored llm_input state and sends a complete
38
+ * audit entry to AxonFlow (provider, model, prompt summary, response
39
+ * summary, token usage, latency).
40
+ */
41
+ export declare function createLlmOutputHandler(client: AxonFlowClient, _config: AxonFlowPluginConfig, callState: Map<string, LLMCallState>): (event: {
42
+ runId: string;
43
+ sessionId: string;
44
+ provider: string;
45
+ model: string;
46
+ assistantTexts: string[];
47
+ lastAssistant?: unknown;
48
+ usage?: {
49
+ input?: number;
50
+ output?: number;
51
+ total?: number;
52
+ };
53
+ }) => Promise<void>;
54
+ export {};
55
+ //# sourceMappingURL=llm-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-audit.d.ts","sourceRoot":"","sources":["../src/llm-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,8DAA8D;AAC9D,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,IAE5B,OAAO;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB,KAAG,IAAI,CAmBT;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,IAEtB,OAAO;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,KAAG,OAAO,CAAC,IAAI,CAAC,CAwBlB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * llm_input and llm_output hooks — LLM call audit logging.
3
+ *
4
+ * Records what the LLM sees (prompt, model, provider) and produces
5
+ * (response, token usage) to AxonFlow's audit trail. These hooks are
6
+ * observe-only (cannot block or modify), so they provide audit
7
+ * evidence, not governance.
8
+ */
9
+ /**
10
+ * Create the llm_input hook handler.
11
+ *
12
+ * Records the prompt, model, and provider at the start of each LLM call.
13
+ * Stores state by runId for correlation with the llm_output handler.
14
+ */
15
+ export function createLlmInputHandler(_client, _config, callState) {
16
+ return (event) => {
17
+ callState.set(event.runId, {
18
+ provider: event.provider,
19
+ model: event.model,
20
+ prompt: event.prompt.slice(0, 500),
21
+ startMs: Date.now(),
22
+ });
23
+ // Prevent unbounded growth: evict entries older than 5 minutes.
24
+ // Handles cases where llm_input fires without a matching llm_output
25
+ // (LLM errors, timeouts, network failures).
26
+ const MAX_AGE_MS = 5 * 60 * 1000;
27
+ const now = Date.now();
28
+ for (const [key, val] of callState) {
29
+ if (now - val.startMs > MAX_AGE_MS) {
30
+ callState.delete(key);
31
+ }
32
+ }
33
+ };
34
+ }
35
+ /**
36
+ * Create the llm_output hook handler.
37
+ *
38
+ * Correlates with the stored llm_input state and sends a complete
39
+ * audit entry to AxonFlow (provider, model, prompt summary, response
40
+ * summary, token usage, latency).
41
+ */
42
+ export function createLlmOutputHandler(client, _config, callState) {
43
+ return async (event) => {
44
+ const inputState = callState.get(event.runId);
45
+ callState.delete(event.runId);
46
+ const responseSummary = event.assistantTexts.join(" ").slice(0, 200);
47
+ const latencyMs = inputState ? Date.now() - inputState.startMs : 0;
48
+ try {
49
+ await client.auditLLMCall(inputState?.provider ?? event.provider, inputState?.model ?? event.model, inputState?.prompt ?? "", responseSummary, {
50
+ prompt_tokens: event.usage?.input ?? 0,
51
+ completion_tokens: event.usage?.output ?? 0,
52
+ total_tokens: event.usage?.total ?? 0,
53
+ }, latencyMs);
54
+ }
55
+ catch {
56
+ // Audit failures are non-fatal
57
+ }
58
+ };
59
+ }
60
+ //# sourceMappingURL=llm-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-audit.js","sourceRoot":"","sources":["../src/llm-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAuB,EACvB,OAA6B,EAC7B,SAAoC;IAEpC,OAAO,CAAC,KASP,EAAQ,EAAE;QACT,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE;YACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;YAClC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB,CAAC,CAAC;QAEH,gEAAgE;QAChE,oEAAoE;QACpE,4CAA4C;QAC5C,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;YACnC,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,UAAU,EAAE,CAAC;gBACnC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAsB,EACtB,OAA6B,EAC7B,SAAoC;IAEpC,OAAO,KAAK,EAAE,KAYb,EAAiB,EAAE;QAClB,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9C,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAE9B,MAAM,eAAe,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACrE,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnE,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,YAAY,CACvB,UAAU,EAAE,QAAQ,IAAI,KAAK,CAAC,QAAQ,EACtC,UAAU,EAAE,KAAK,IAAI,KAAK,CAAC,KAAK,EAChC,UAAU,EAAE,MAAM,IAAI,EAAE,EACxB,eAAe,EACf;gBACE,aAAa,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC;gBACtC,iBAAiB,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;gBAC3C,YAAY,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC;aACtC,EACD,SAAS,CACV,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,+BAA+B;QACjC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * message_sending hook — outbound message governance.
3
+ *
4
+ * Scans messages before they reach the user's channel (Telegram,
5
+ * Discord, Slack, WhatsApp). Can cancel messages containing PII/secrets
6
+ * or redact sensitive content.
7
+ */
8
+ import type { AxonFlowClient } from "./axonflow-client.js";
9
+ import type { AxonFlowPluginConfig } from "./config.js";
10
+ /**
11
+ * Create the message_sending hook handler.
12
+ *
13
+ * Evaluates outbound message content against AxonFlow output policies.
14
+ * Can cancel (prevent sending) or redact (modify content) before delivery.
15
+ * Respects config.onError for fail-open/fail-closed behavior.
16
+ */
17
+ export declare function createMessageSendingHandler(client: AxonFlowClient, config: AxonFlowPluginConfig): (event: {
18
+ to: string;
19
+ content: string;
20
+ metadata?: Record<string, unknown>;
21
+ }) => Promise<{
22
+ content?: string;
23
+ cancel?: boolean;
24
+ } | undefined>;
25
+ //# sourceMappingURL=message-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-guard.d.ts","sourceRoot":"","sources":["../src/message-guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,oBAAoB,IAEd,OAAO;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,KAAG,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC,CAoChE"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * message_sending hook — outbound message governance.
3
+ *
4
+ * Scans messages before they reach the user's channel (Telegram,
5
+ * Discord, Slack, WhatsApp). Can cancel messages containing PII/secrets
6
+ * or redact sensitive content.
7
+ */
8
+ /**
9
+ * Create the message_sending hook handler.
10
+ *
11
+ * Evaluates outbound message content against AxonFlow output policies.
12
+ * Can cancel (prevent sending) or redact (modify content) before delivery.
13
+ * Respects config.onError for fail-open/fail-closed behavior.
14
+ */
15
+ export function createMessageSendingHandler(client, config) {
16
+ return async (event) => {
17
+ if (!event.content) {
18
+ return undefined;
19
+ }
20
+ let check;
21
+ try {
22
+ check = await client.mcpCheckOutput("openclaw.message_sending", event.content);
23
+ }
24
+ catch {
25
+ if (config.onError === "allow") {
26
+ return undefined; // Fail-open: allow message through ungoverned
27
+ }
28
+ // Fail-closed: cancel the message rather than send ungoverned
29
+ return { cancel: true };
30
+ }
31
+ if (!check.allowed) {
32
+ return {
33
+ cancel: true,
34
+ };
35
+ }
36
+ if (check.redacted_data != null) {
37
+ return {
38
+ content: typeof check.redacted_data === "string"
39
+ ? check.redacted_data
40
+ : JSON.stringify(check.redacted_data),
41
+ };
42
+ }
43
+ return undefined;
44
+ };
45
+ }
46
+ //# sourceMappingURL=message-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-guard.js","sourceRoot":"","sources":["../src/message-guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,MAAsB,EACtB,MAA4B;IAE5B,OAAO,KAAK,EAAE,KAIb,EAA+D,EAAE;QAChE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,KAAK,CAAC;QACV,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,MAAM,CAAC,cAAc,CACjC,0BAA0B,EAC1B,KAAK,CAAC,OAAO,CACd,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;gBAC/B,OAAO,SAAS,CAAC,CAAC,8CAA8C;YAClE,CAAC;YACD,8DAA8D;YAC9D,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO;gBACL,MAAM,EAAE,IAAI;aACb,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YAChC,OAAO;gBACL,OAAO,EACL,OAAO,KAAK,CAAC,aAAa,KAAK,QAAQ;oBACrC,CAAC,CAAC,KAAK,CAAC,aAAa;oBACrB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC;aAC1C,CAAC;QACJ,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,96 @@
1
+ {
2
+ "id": "axonflow-governance",
3
+ "uiHints": {
4
+ "endpoint": {
5
+ "label": "AxonFlow Endpoint",
6
+ "placeholder": "http://localhost:8080",
7
+ "help": "URL of your AxonFlow agent gateway"
8
+ },
9
+ "clientId": {
10
+ "label": "Client ID",
11
+ "placeholder": "your-client-id",
12
+ "help": "AxonFlow authentication client ID"
13
+ },
14
+ "clientSecret": {
15
+ "label": "Client Secret",
16
+ "sensitive": true,
17
+ "placeholder": "your-secret",
18
+ "help": "AxonFlow authentication client secret"
19
+ },
20
+ "highRiskTools": {
21
+ "label": "High-Risk Tools",
22
+ "placeholder": "web_fetch, message",
23
+ "help": "Comma-separated tool names requiring human approval even when policy allows"
24
+ },
25
+ "governedTools": {
26
+ "label": "Governed Tools",
27
+ "placeholder": "(empty = all tools)",
28
+ "help": "Only govern these tools. Empty means all tools are governed.",
29
+ "advanced": true
30
+ },
31
+ "excludedTools": {
32
+ "label": "Excluded Tools",
33
+ "placeholder": "(empty = none excluded)",
34
+ "help": "Exclude these tools from governance. Takes precedence over governedTools.",
35
+ "advanced": true
36
+ },
37
+ "defaultOperation": {
38
+ "label": "Default Operation",
39
+ "placeholder": "execute",
40
+ "help": "Operation type for mcp_check_input: 'execute' (default) or 'query' for read-only tools",
41
+ "advanced": true
42
+ }
43
+ },
44
+ "configSchema": {
45
+ "type": "object",
46
+ "additionalProperties": false,
47
+ "properties": {
48
+ "endpoint": {
49
+ "type": "string"
50
+ },
51
+ "clientId": {
52
+ "type": "string"
53
+ },
54
+ "clientSecret": {
55
+ "type": "string"
56
+ },
57
+ "highRiskTools": {
58
+ "type": "array",
59
+ "items": {
60
+ "type": "string"
61
+ }
62
+ },
63
+ "governedTools": {
64
+ "type": "array",
65
+ "items": {
66
+ "type": "string"
67
+ }
68
+ },
69
+ "excludedTools": {
70
+ "type": "array",
71
+ "items": {
72
+ "type": "string"
73
+ }
74
+ },
75
+ "defaultOperation": {
76
+ "type": "string",
77
+ "enum": [
78
+ "execute",
79
+ "query"
80
+ ]
81
+ },
82
+ "onError": {
83
+ "type": "string",
84
+ "enum": [
85
+ "block",
86
+ "allow"
87
+ ]
88
+ }
89
+ },
90
+ "required": [
91
+ "endpoint",
92
+ "clientId",
93
+ "clientSecret"
94
+ ]
95
+ }
96
+ }
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@axonflow/openclaw",
3
+ "version": "0.1.0",
4
+ "description": "Policy enforcement, approval gates, and audit trails for OpenClaw — govern tool inputs before execution, scan outbound messages for PII/secrets, and record agent activity for review and compliance",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "policies",
17
+ "README.md",
18
+ "CHANGELOG.md",
19
+ "LICENSE",
20
+ "openclaw.plugin.json"
21
+ ],
22
+ "scripts": {
23
+ "clean": "rm -rf dist",
24
+ "prebuild": "npm run clean",
25
+ "build": "tsc",
26
+ "lint": "eslint src/ tests/",
27
+ "test": "jest",
28
+ "test:coverage": "jest --coverage",
29
+ "prepublishOnly": "npm run build"
30
+ },
31
+ "keywords": [
32
+ "axonflow",
33
+ "openclaw",
34
+ "openclaw-security",
35
+ "openclaw-plugin",
36
+ "ai-agent-security",
37
+ "ai-safety",
38
+ "governance",
39
+ "policy-enforcement",
40
+ "pii-detection",
41
+ "pii-redaction",
42
+ "data-exfiltration",
43
+ "prompt-injection",
44
+ "reverse-shell",
45
+ "credential-detection",
46
+ "audit-logging",
47
+ "audit-trail",
48
+ "tool-governance",
49
+ "supply-chain-security",
50
+ "compliance",
51
+ "ssrf-protection"
52
+ ],
53
+ "author": "AxonFlow Team <team@getaxonflow.com>",
54
+ "license": "MIT",
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/getaxonflow/axonflow-openclaw-plugin.git"
58
+ },
59
+ "homepage": "https://docs.getaxonflow.com/docs/integration/openclaw/",
60
+ "bugs": {
61
+ "url": "https://github.com/getaxonflow/axonflow-openclaw-plugin/issues"
62
+ },
63
+ "peerDependencies": {
64
+ "@axonflow/sdk": ">=4.3.0",
65
+ "openclaw": ">=1.0.0"
66
+ },
67
+ "devDependencies": {
68
+ "@types/jest": "^29.5.0",
69
+ "@types/node": "^20.0.0",
70
+ "eslint": "^9.0.0",
71
+ "jest": "^29.7.0",
72
+ "ts-jest": "^29.1.0",
73
+ "typescript": "^5.4.0",
74
+ "typescript-eslint": "^8.58.0"
75
+ },
76
+ "publishConfig": {
77
+ "access": "public"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "@axonflow/sdk": {
81
+ "optional": true
82
+ }
83
+ },
84
+ "openclaw": {
85
+ "extensions": [
86
+ "./dist/index.js"
87
+ ],
88
+ "compat": {
89
+ "pluginApi": ">=2026.3.22",
90
+ "minGatewayVersion": "2026.3.22"
91
+ }
92
+ }
93
+ }