@inquiryon/openclaw-amp-governance 1.0.4 → 1.0.5

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 (2) hide show
  1. package/index.js +39 -1
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as fs from 'fs';
2
+ import { exec } from 'child_process';
2
3
 
3
4
  console.log('[AMP Governance] Plugin module loaded — Phase 4.');
4
5
 
@@ -23,6 +24,9 @@ try {
23
24
  // Module-level instance cache so we only init once per process lifetime
24
25
  let _instanceId = null;
25
26
 
27
+ // Last known sender — populated by message_received, used for HITL notifications
28
+ let _lastSender = null; // { from: string, channelId: string }
29
+
26
30
  function readSession() {
27
31
  try {
28
32
  return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
@@ -111,6 +115,23 @@ async function ampLog(instanceId, message, level = 'INFO') {
111
115
  }
112
116
  }
113
117
 
118
+ // ── PROACTIVE NOTIFICATIONS ──────────────────────────────────────────────────
119
+
120
+ /**
121
+ * Send a proactive WhatsApp message to the user without waiting for the agent
122
+ * to finish. Uses the openclaw CLI so we don't need to reverse-engineer the
123
+ * gateway REST API. Fire-and-forget — errors are logged but never thrown.
124
+ */
125
+ function notifyUser(sender, message) {
126
+ if (!sender?.from || !sender?.channelId) return;
127
+ const safeMsg = message.replace(/"/g, '\\"');
128
+ const cmd = `openclaw message send --channel ${sender.channelId} --target "${sender.from}" --message "${safeMsg}"`;
129
+ console.log(`[AMP Governance] Sending notification to ${sender.from}: ${message}`);
130
+ exec(cmd, (err) => {
131
+ if (err) console.warn('[AMP Governance] notifyUser failed:', err.message);
132
+ });
133
+ }
134
+
114
135
  // ── HITL POLICY ENGINE ───────────────────────────────────────────────────────
115
136
 
116
137
  /**
@@ -209,12 +230,16 @@ async function checkToolPolicy(instanceId, tool, params) {
209
230
  console.log(`[AMP Governance] HITL required for "${tool}" — waiting for human decision...`);
210
231
  await ampLog(instanceId, `HITL requested for tool: ${tool} — awaiting human approval in AMP`, 'WARN');
211
232
 
233
+ // Notify the user immediately so they aren't staring at silence
234
+ notifyUser(_lastSender, `I need a reviewer to approve "${tool}" before I can proceed. I'll follow up once the decision is made — this may take a few minutes.`);
235
+
212
236
  let decision;
213
237
  try {
214
238
  decision = await pollHitlDecision(instanceId);
215
239
  } catch (err) {
216
240
  const msg = `Tool call "${tool}" timed out waiting for human approval — blocked.`;
217
241
  await ampLog(instanceId, msg, 'ERROR');
242
+ notifyUser(_lastSender, `No response from the reviewer — "${tool}" was blocked due to timeout.`);
218
243
  return { block: true, blockReason: msg };
219
244
  }
220
245
 
@@ -223,11 +248,13 @@ async function checkToolPolicy(instanceId, tool, params) {
223
248
 
224
249
  if (resolution === 'approve' || resolution === 'approved') {
225
250
  await ampLog(instanceId, `HITL approved: ${tool} | workitem: ${decision.workitem_id}`);
251
+ notifyUser(_lastSender, `The reviewer approved "${tool}" — continuing now.`);
226
252
  return {};
227
253
  }
228
254
 
229
255
  if (resolution === 'modify' || resolution === 'modified') {
230
256
  await ampLog(instanceId, `HITL approved with modification: ${tool} | workitem: ${decision.workitem_id}`);
257
+ notifyUser(_lastSender, `The reviewer approved "${tool}" (with modifications) — continuing now.`);
231
258
  return {};
232
259
  }
233
260
 
@@ -235,6 +262,7 @@ async function checkToolPolicy(instanceId, tool, params) {
235
262
  const info = decision.information ? ` Reviewer note: ${decision.information}` : '';
236
263
  const msg = `Tool call "${tool}" was rejected by a human reviewer.${info}`;
237
264
  await ampLog(instanceId, `HITL rejected: ${tool} | workitem: ${decision.workitem_id}`, 'WARN');
265
+ notifyUser(_lastSender, `The reviewer rejected "${tool}".${info}`);
238
266
  return { block: true, blockReason: msg };
239
267
  }
240
268
 
@@ -360,6 +388,14 @@ export default {
360
388
  register(api) {
361
389
  api.logger.info('AMP Governance registered. Phase 4 - eval policy enforcement active.');
362
390
 
391
+ // ── INBOUND MESSAGE: cache sender for HITL notifications ─────────────────
392
+ api.on('message_received', (event, ctx) => {
393
+ if (event.from && ctx.channelId) {
394
+ _lastSender = { from: event.from, channelId: ctx.channelId };
395
+ console.log(`[AMP Governance] Sender cached: ${event.from} on ${ctx.channelId}`);
396
+ }
397
+ });
398
+
363
399
  // ── BEFORE TOOL CALL: governance check + logging ─────────────────────────
364
400
  api.on('before_tool_call', async (event) => {
365
401
  const tool = event.toolName || event.name || 'unknown-tool';
@@ -398,7 +434,7 @@ export default {
398
434
  await ampLog(instanceId, message, event.success === false ? 'ERROR' : 'INFO');
399
435
  });
400
436
 
401
- // ── OUTBOUND MESSAGE LOGGING ──────────────────────────────────────────────
437
+ // ── OUTBOUND MESSAGE LOGGING + PREFIX ────────────────────────────────────
402
438
  api.on('message_sending', async (event) => {
403
439
  const instanceId = _instanceId || readSession()?.instanceId;
404
440
  const msgText = event.content || event.text || event.message || JSON.stringify(event);
@@ -407,6 +443,8 @@ export default {
407
443
  if (instanceId) {
408
444
  await ampLog(instanceId, `Agent reply: ${preview}`);
409
445
  }
446
+ // Prefix every outbound message so the user can distinguish agent replies
447
+ return { content: `[OpenClaw]\n${msgText}` };
410
448
  });
411
449
  },
412
450
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inquiryon/openclaw-amp-governance",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "AMP governance plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "files": [