@agentapprove/openclaw 0.1.6 → 0.1.7
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/dist/index.js +154 -58
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,22 +35,77 @@ function ensureLogFile() {
|
|
|
35
35
|
if (stat.size > MAX_SIZE) {
|
|
36
36
|
const content = readFileSync(DEBUG_LOG_PATH, "utf-8");
|
|
37
37
|
writeFileSync(DEBUG_LOG_PATH, content.slice(-KEEP_SIZE), { mode: 384 });
|
|
38
|
-
|
|
39
|
-
appendFileSync(DEBUG_LOG_PATH, `[${ts}] [openclaw-plugin] Log rotated (exceeded 5MB)
|
|
38
|
+
appendFileSync(DEBUG_LOG_PATH, `[${localTimestamp()}] [debug] Log rotated (exceeded 5MB, kept last 2MB)
|
|
40
39
|
`);
|
|
41
40
|
}
|
|
42
41
|
} catch {
|
|
43
42
|
}
|
|
44
43
|
}
|
|
44
|
+
function localTimestamp() {
|
|
45
|
+
const d = /* @__PURE__ */ new Date();
|
|
46
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
47
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
48
|
+
}
|
|
45
49
|
function debugLog(message, hookName = "openclaw-plugin") {
|
|
46
50
|
try {
|
|
47
51
|
ensureLogFile();
|
|
48
|
-
|
|
49
|
-
appendFileSync(DEBUG_LOG_PATH, `[${ts}] [${hookName}] ${message}
|
|
52
|
+
appendFileSync(DEBUG_LOG_PATH, `[${localTimestamp()}] [${hookName}] ${message}
|
|
50
53
|
`);
|
|
51
54
|
} catch {
|
|
52
55
|
}
|
|
53
56
|
}
|
|
57
|
+
function debugLogRawInline(data) {
|
|
58
|
+
try {
|
|
59
|
+
ensureLogFile();
|
|
60
|
+
appendFileSync(DEBUG_LOG_PATH, `${data}
|
|
61
|
+
`);
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function safeStringify(value) {
|
|
66
|
+
try {
|
|
67
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
68
|
+
return JSON.stringify(value, (_key, current) => {
|
|
69
|
+
if (typeof current === "bigint") return `BigInt(${current.toString()})`;
|
|
70
|
+
if (typeof current === "function") {
|
|
71
|
+
const fn = current;
|
|
72
|
+
return `[Function ${fn.name || "anonymous"}]`;
|
|
73
|
+
}
|
|
74
|
+
if (typeof current === "symbol") return current.toString();
|
|
75
|
+
if (current instanceof Error) {
|
|
76
|
+
return {
|
|
77
|
+
name: current.name,
|
|
78
|
+
message: current.message,
|
|
79
|
+
stack: current.stack
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (current && typeof current === "object") {
|
|
83
|
+
const obj = current;
|
|
84
|
+
if (seen.has(obj)) return "[Circular]";
|
|
85
|
+
seen.add(obj);
|
|
86
|
+
}
|
|
87
|
+
return current;
|
|
88
|
+
}) ?? "null";
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
91
|
+
return `"[Unserializable: ${msg}]"`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function debugLogRaw(data, hookName = "openclaw-plugin") {
|
|
95
|
+
try {
|
|
96
|
+
ensureLogFile();
|
|
97
|
+
const ts = localTimestamp();
|
|
98
|
+
const rawData = typeof data === "string" ? data : safeStringify(data);
|
|
99
|
+
appendFileSync(
|
|
100
|
+
DEBUG_LOG_PATH,
|
|
101
|
+
`[${ts}] [${hookName}] === RAW INPUT ===
|
|
102
|
+
${rawData}
|
|
103
|
+
[${ts}] [${hookName}] === END RAW ===
|
|
104
|
+
`
|
|
105
|
+
);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
54
109
|
|
|
55
110
|
// src/e2e-crypto.ts
|
|
56
111
|
function keyId(keyHex) {
|
|
@@ -331,7 +386,7 @@ function loadConfig(openclawConfig, logger) {
|
|
|
331
386
|
failBehavior,
|
|
332
387
|
privacyTier,
|
|
333
388
|
debug,
|
|
334
|
-
hookVersion: "1.1.
|
|
389
|
+
hookVersion: "1.1.4",
|
|
335
390
|
agentName,
|
|
336
391
|
e2eEnabled,
|
|
337
392
|
e2eUserKey,
|
|
@@ -585,11 +640,11 @@ function getLocalConfigSetAt() {
|
|
|
585
640
|
|
|
586
641
|
// src/api-client.ts
|
|
587
642
|
var cachedPluginHash;
|
|
588
|
-
function getPluginHash(pluginPath, debug = false) {
|
|
643
|
+
function getPluginHash(pluginPath, debug = false, hookName = "openclaw-plugin") {
|
|
589
644
|
if (!cachedPluginHash) {
|
|
590
645
|
cachedPluginHash = computePluginHash(pluginPath);
|
|
591
646
|
if (cachedPluginHash && debug) {
|
|
592
|
-
debugLog(`Plugin hash computed: ${cachedPluginHash.slice(0, 16)}
|
|
647
|
+
debugLog(`Plugin hash computed: ${cachedPluginHash.slice(0, 16)}...`, hookName);
|
|
593
648
|
}
|
|
594
649
|
}
|
|
595
650
|
return cachedPluginHash || "";
|
|
@@ -631,7 +686,7 @@ function httpPost(url, body, headers, timeoutMs) {
|
|
|
631
686
|
req.end();
|
|
632
687
|
});
|
|
633
688
|
}
|
|
634
|
-
async function sendApprovalRequest(request, config, pluginPath) {
|
|
689
|
+
async function sendApprovalRequest(request, config, pluginPath, hookName = "openclaw-plugin") {
|
|
635
690
|
if (!config.token) {
|
|
636
691
|
throw new Error("No Agent Approve token configured");
|
|
637
692
|
}
|
|
@@ -639,13 +694,13 @@ async function sendApprovalRequest(request, config, pluginPath) {
|
|
|
639
694
|
if (config.e2eEnabled && config.e2eUserKey) {
|
|
640
695
|
payload = applyApprovalE2E(request, config.e2eUserKey, config.e2eServerKey);
|
|
641
696
|
if (config.debug) {
|
|
642
|
-
debugLog("E2E encryption applied to approval request");
|
|
697
|
+
debugLog("E2E encryption applied to approval request", hookName);
|
|
643
698
|
}
|
|
644
699
|
} else {
|
|
645
700
|
payload = applyPrivacyFilter(request, config.privacyTier);
|
|
646
701
|
}
|
|
647
702
|
const bodyStr = JSON.stringify(payload);
|
|
648
|
-
const pluginHash = getPluginHash(pluginPath, config.debug);
|
|
703
|
+
const pluginHash = getPluginHash(pluginPath, config.debug, hookName);
|
|
649
704
|
const hmacHeaders = buildHMACHeaders(bodyStr, config.token, config.hookVersion, pluginHash);
|
|
650
705
|
const headers = {
|
|
651
706
|
"Authorization": `Bearer ${config.token}`,
|
|
@@ -653,11 +708,14 @@ async function sendApprovalRequest(request, config, pluginPath) {
|
|
|
653
708
|
};
|
|
654
709
|
const url = `${config.apiUrl}/${config.apiVersion}/approve`;
|
|
655
710
|
if (config.debug) {
|
|
656
|
-
debugLog(`
|
|
711
|
+
debugLog(`Requesting approval from ${url}`, hookName);
|
|
712
|
+
debugLog(`=== SENT TO ${url} ===`, hookName);
|
|
713
|
+
debugLogRawInline(bodyStr);
|
|
714
|
+
debugLog("=== END SENT ===", hookName);
|
|
657
715
|
}
|
|
658
716
|
const response = await httpPost(url, bodyStr, headers, config.timeout * 1e3);
|
|
659
717
|
if (config.debug) {
|
|
660
|
-
debugLog(`Response
|
|
718
|
+
debugLog(`Response: ${response.body || "<empty>"}`, hookName);
|
|
661
719
|
}
|
|
662
720
|
if (response.status !== 200) {
|
|
663
721
|
throw new Error(`API returned status ${response.status}: ${response.body.slice(0, 200)}`);
|
|
@@ -671,23 +729,23 @@ async function sendApprovalRequest(request, config, pluginPath) {
|
|
|
671
729
|
processConfigSync(parsed, config);
|
|
672
730
|
return parsed;
|
|
673
731
|
}
|
|
674
|
-
async function sendEvent(event, config, pluginPath) {
|
|
732
|
+
async function sendEvent(event, config, pluginPath, hookName = "openclaw-plugin") {
|
|
675
733
|
if (!config.token) return;
|
|
676
734
|
const eventType = event.eventType;
|
|
677
735
|
const toolName = event.toolName;
|
|
678
736
|
if (config.debug) {
|
|
679
|
-
debugLog(`Sending ${eventType || "event"}${toolName ? ` (${toolName})` : ""} (privacy: ${config.privacyTier})
|
|
737
|
+
debugLog(`Sending ${eventType || "event"}${toolName ? ` (${toolName})` : ""} (privacy: ${config.privacyTier})`, hookName);
|
|
680
738
|
}
|
|
681
739
|
try {
|
|
682
740
|
let payload = applyEventPrivacyFilter(event, config.privacyTier);
|
|
683
741
|
if (config.e2eEnabled && config.e2eUserKey) {
|
|
684
742
|
payload = applyEventE2E(payload, config.e2eUserKey);
|
|
685
743
|
if (config.debug) {
|
|
686
|
-
debugLog(`E2E applied to event (type=${eventType})
|
|
744
|
+
debugLog(`E2E applied to event (type=${eventType})`, hookName);
|
|
687
745
|
}
|
|
688
746
|
}
|
|
689
747
|
const bodyStr = JSON.stringify(payload);
|
|
690
|
-
const pluginHash = getPluginHash(pluginPath, config.debug);
|
|
748
|
+
const pluginHash = getPluginHash(pluginPath, config.debug, hookName);
|
|
691
749
|
const hmacHeaders = buildHMACHeaders(bodyStr, config.token, config.hookVersion, pluginHash);
|
|
692
750
|
const headers = {
|
|
693
751
|
"Authorization": `Bearer ${config.token}`,
|
|
@@ -695,13 +753,13 @@ async function sendEvent(event, config, pluginPath) {
|
|
|
695
753
|
};
|
|
696
754
|
const url = `${config.apiUrl}/${config.apiVersion}/events`;
|
|
697
755
|
if (config.debug) {
|
|
698
|
-
debugLog(`=== SENT TO ${url}
|
|
699
|
-
|
|
700
|
-
debugLog("=== END SENT ===");
|
|
756
|
+
debugLog(`=== SENT TO ${url} ===`, hookName);
|
|
757
|
+
debugLogRawInline(bodyStr);
|
|
758
|
+
debugLog("=== END SENT ===", hookName);
|
|
701
759
|
}
|
|
702
760
|
const response = await httpPost(url, bodyStr, headers, 5e3);
|
|
703
761
|
if (config.debug) {
|
|
704
|
-
debugLog(`send_event response: ${response.body
|
|
762
|
+
debugLog(`send_event response: ${response.body || "<empty>"}`, hookName);
|
|
705
763
|
}
|
|
706
764
|
if (response.status === 200) {
|
|
707
765
|
try {
|
|
@@ -712,7 +770,7 @@ async function sendEvent(event, config, pluginPath) {
|
|
|
712
770
|
}
|
|
713
771
|
} catch (err) {
|
|
714
772
|
if (config.debug) {
|
|
715
|
-
debugLog(`Failed to send ${eventType || "event"}: ${err instanceof Error ? err.message : String(err)}
|
|
773
|
+
debugLog(`Failed to send ${eventType || "event"}: ${err instanceof Error ? err.message : String(err)}`, hookName);
|
|
716
774
|
}
|
|
717
775
|
}
|
|
718
776
|
}
|
|
@@ -728,6 +786,19 @@ var gatewaySessionId = randomBytes2(12).toString("hex");
|
|
|
728
786
|
var DEDUP_WINDOW_MS = 1200;
|
|
729
787
|
var DEDUP_MAX_SIZE = 300;
|
|
730
788
|
var recentCompletions = /* @__PURE__ */ new Map();
|
|
789
|
+
var HOOK_PLUGIN = "openclaw-plugin";
|
|
790
|
+
var HOOK_BEFORE_TOOL = "openclaw-before-tool";
|
|
791
|
+
var HOOK_AFTER_TOOL = "openclaw-after-tool";
|
|
792
|
+
var HOOK_SESSION_START = "openclaw-session-start";
|
|
793
|
+
var HOOK_SESSION_END = "openclaw-session-end";
|
|
794
|
+
var HOOK_LLM_INPUT = "openclaw-llm-input";
|
|
795
|
+
var HOOK_LLM_OUTPUT = "openclaw-llm-output";
|
|
796
|
+
var HOOK_AGENT_END = "openclaw-agent-end";
|
|
797
|
+
var HOOK_BEFORE_COMPACTION = "openclaw-before-compaction";
|
|
798
|
+
var HOOK_SUBAGENT_SPAWNED = "openclaw-subagent-spawned";
|
|
799
|
+
var HOOK_SUBAGENT_ENDED = "openclaw-subagent-ended";
|
|
800
|
+
var HOOK_COMMAND = "openclaw-command";
|
|
801
|
+
var HOOK_MESSAGE = "openclaw-message";
|
|
731
802
|
function resolveConversationId() {
|
|
732
803
|
return gatewaySessionId;
|
|
733
804
|
}
|
|
@@ -826,10 +897,10 @@ function extractResultPreview(toolName, params, result, maxLen = 300) {
|
|
|
826
897
|
if (str.length <= maxLen) return str;
|
|
827
898
|
return str.slice(0, maxLen) + "...";
|
|
828
899
|
}
|
|
829
|
-
function handleFailBehavior(config, error, toolName, logger) {
|
|
900
|
+
function handleFailBehavior(config, error, toolName, logger, hookName = HOOK_PLUGIN) {
|
|
830
901
|
logger.warn(`Agent Approve API error for tool "${toolName}": ${error.message}`);
|
|
831
902
|
if (config.debug) {
|
|
832
|
-
debugLog(`API error: ${error.message}, failBehavior: ${config.failBehavior}
|
|
903
|
+
debugLog(`API error: ${error.message}, failBehavior: ${config.failBehavior}`, hookName);
|
|
833
904
|
}
|
|
834
905
|
switch (config.failBehavior) {
|
|
835
906
|
case "deny":
|
|
@@ -854,14 +925,22 @@ function register(api) {
|
|
|
854
925
|
if (config.debug) {
|
|
855
926
|
const e2eStatus = !config.e2eEnabled ? "disabled" : !config.e2eUserKey ? "enabled (key missing)" : "enabled";
|
|
856
927
|
debugLog(
|
|
857
|
-
`Plugin loaded: v${config.hookVersion}, api=${config.apiUrl}, privacy=${config.privacyTier}, e2e=${e2eStatus}, debug=${config.debug}
|
|
928
|
+
`Plugin loaded: v${config.hookVersion}, api=${config.apiUrl}, privacy=${config.privacyTier}, e2e=${e2eStatus}, debug=${config.debug}`,
|
|
929
|
+
HOOK_PLUGIN
|
|
858
930
|
);
|
|
859
|
-
debugLog(`Full config: agent=${config.agentName}, timeout=${config.timeout}s, fail=${config.failBehavior}
|
|
931
|
+
debugLog(`Full config: agent=${config.agentName}, timeout=${config.timeout}s, fail=${config.failBehavior}`, HOOK_PLUGIN);
|
|
860
932
|
}
|
|
861
933
|
api.on("before_tool_call", async (event, ctx) => {
|
|
934
|
+
if (config.debug) {
|
|
935
|
+
debugLogRaw({ event, ctx }, HOOK_BEFORE_TOOL);
|
|
936
|
+
debugLog("Started before_tool_call hook", HOOK_BEFORE_TOOL);
|
|
937
|
+
}
|
|
862
938
|
const conversationId = resolveConversationId();
|
|
863
939
|
const { toolType, displayName } = classifyTool(event.toolName, event.params);
|
|
864
940
|
const command = extractCommand(event.toolName, event.params);
|
|
941
|
+
if (config.debug) {
|
|
942
|
+
debugLog(`Tool: ${displayName} (${toolType}) [agent: ${config.agentName}]`, HOOK_BEFORE_TOOL);
|
|
943
|
+
}
|
|
865
944
|
const request = {
|
|
866
945
|
toolName: displayName,
|
|
867
946
|
toolType,
|
|
@@ -875,15 +954,18 @@ function register(api) {
|
|
|
875
954
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
876
955
|
};
|
|
877
956
|
try {
|
|
878
|
-
const response = await sendApprovalRequest(request, config, pluginFilePath);
|
|
957
|
+
const response = await sendApprovalRequest(request, config, pluginFilePath, HOOK_BEFORE_TOOL);
|
|
958
|
+
if (config.debug) {
|
|
959
|
+
debugLog(`Decision: ${response.decision}, Reason: ${response.reason || ""}`, HOOK_BEFORE_TOOL);
|
|
960
|
+
}
|
|
879
961
|
if (response.decision === "approve" || response.decision === "allow") {
|
|
880
962
|
if (config.debug) {
|
|
881
|
-
debugLog(
|
|
963
|
+
debugLog("Tool approved", HOOK_BEFORE_TOOL);
|
|
882
964
|
}
|
|
883
965
|
return void 0;
|
|
884
966
|
}
|
|
885
967
|
if (config.debug) {
|
|
886
|
-
debugLog(
|
|
968
|
+
debugLog("Tool denied", HOOK_BEFORE_TOOL);
|
|
887
969
|
}
|
|
888
970
|
return {
|
|
889
971
|
block: true,
|
|
@@ -894,15 +976,19 @@ function register(api) {
|
|
|
894
976
|
config,
|
|
895
977
|
error instanceof Error ? error : new Error(String(error)),
|
|
896
978
|
event.toolName,
|
|
897
|
-
api.logger
|
|
979
|
+
api.logger,
|
|
980
|
+
HOOK_BEFORE_TOOL
|
|
898
981
|
);
|
|
899
982
|
}
|
|
900
983
|
});
|
|
901
984
|
api.on("after_tool_call", async (event, ctx) => {
|
|
985
|
+
if (config.debug) {
|
|
986
|
+
debugLogRaw({ event, ctx }, HOOK_AFTER_TOOL);
|
|
987
|
+
}
|
|
902
988
|
const conversationId = resolveConversationId();
|
|
903
989
|
if (isDuplicateCompletion(event.toolName, event.params)) {
|
|
904
990
|
if (config.debug) {
|
|
905
|
-
debugLog(`Skipping duplicate tool_complete for "${event.toolName}"
|
|
991
|
+
debugLog(`Skipping duplicate tool_complete for "${event.toolName}"`, HOOK_AFTER_TOOL);
|
|
906
992
|
}
|
|
907
993
|
return;
|
|
908
994
|
}
|
|
@@ -921,13 +1007,14 @@ function register(api) {
|
|
|
921
1007
|
response: event.error || resultPreview || void 0,
|
|
922
1008
|
durationMs: event.durationMs,
|
|
923
1009
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
924
|
-
}, config, pluginFilePath);
|
|
1010
|
+
}, config, pluginFilePath, HOOK_AFTER_TOOL);
|
|
925
1011
|
});
|
|
926
1012
|
api.on("session_start", async (event, ctx) => {
|
|
927
|
-
const conversationId = resolveConversationId();
|
|
928
1013
|
if (config.debug) {
|
|
929
|
-
|
|
1014
|
+
debugLogRaw({ event, ctx }, HOOK_SESSION_START);
|
|
1015
|
+
debugLog(`Session started: ${event.sessionId}${event.resumedFrom ? ` (resumed from ${event.resumedFrom})` : ""}`, HOOK_SESSION_START);
|
|
930
1016
|
}
|
|
1017
|
+
const conversationId = resolveConversationId();
|
|
931
1018
|
void sendEvent({
|
|
932
1019
|
eventType: "session_start",
|
|
933
1020
|
agent: config.agentName,
|
|
@@ -935,13 +1022,14 @@ function register(api) {
|
|
|
935
1022
|
sessionId: conversationId,
|
|
936
1023
|
conversationId,
|
|
937
1024
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
938
|
-
}, config, pluginFilePath);
|
|
1025
|
+
}, config, pluginFilePath, HOOK_SESSION_START);
|
|
939
1026
|
});
|
|
940
1027
|
api.on("session_end", async (event, ctx) => {
|
|
941
|
-
const conversationId = resolveConversationId();
|
|
942
1028
|
if (config.debug) {
|
|
943
|
-
|
|
1029
|
+
debugLogRaw({ event, ctx }, HOOK_SESSION_END);
|
|
1030
|
+
debugLog(`Session ended: ${event.sessionId} (${event.messageCount} messages, ${event.durationMs ?? "?"}ms)`, HOOK_SESSION_END);
|
|
944
1031
|
}
|
|
1032
|
+
const conversationId = resolveConversationId();
|
|
945
1033
|
void sendEvent({
|
|
946
1034
|
eventType: "session_end",
|
|
947
1035
|
agent: config.agentName,
|
|
@@ -951,13 +1039,14 @@ function register(api) {
|
|
|
951
1039
|
durationMs: event.durationMs,
|
|
952
1040
|
sessionStats: { messageCount: event.messageCount },
|
|
953
1041
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
954
|
-
}, config, pluginFilePath);
|
|
1042
|
+
}, config, pluginFilePath, HOOK_SESSION_END);
|
|
955
1043
|
});
|
|
956
1044
|
api.on("llm_input", async (event, ctx) => {
|
|
957
|
-
const conversationId = resolveConversationId();
|
|
958
1045
|
if (config.debug) {
|
|
959
|
-
|
|
1046
|
+
debugLogRaw({ event, ctx }, HOOK_LLM_INPUT);
|
|
1047
|
+
debugLog(`LLM input: model=${event.model}, prompt length=${event.prompt?.length ?? 0}`, HOOK_LLM_INPUT);
|
|
960
1048
|
}
|
|
1049
|
+
const conversationId = resolveConversationId();
|
|
961
1050
|
void sendEvent({
|
|
962
1051
|
eventType: "user_prompt",
|
|
963
1052
|
agent: config.agentName,
|
|
@@ -968,14 +1057,15 @@ function register(api) {
|
|
|
968
1057
|
prompt: event.prompt,
|
|
969
1058
|
textLength: event.prompt?.length ?? 0,
|
|
970
1059
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
971
|
-
}, config, pluginFilePath);
|
|
1060
|
+
}, config, pluginFilePath, HOOK_LLM_INPUT);
|
|
972
1061
|
});
|
|
973
1062
|
api.on("llm_output", async (event, ctx) => {
|
|
974
1063
|
const conversationId = resolveConversationId();
|
|
975
1064
|
const responseText = event.assistantTexts?.join("\n") || "";
|
|
976
1065
|
const textLength = responseText.length;
|
|
977
1066
|
if (config.debug) {
|
|
978
|
-
|
|
1067
|
+
debugLogRaw({ event, ctx }, HOOK_LLM_OUTPUT);
|
|
1068
|
+
debugLog(`LLM output: model=${event.model}, length=${textLength}${event.usage?.total ? `, tokens=${event.usage.total}` : ""}`, HOOK_LLM_OUTPUT);
|
|
979
1069
|
}
|
|
980
1070
|
void sendEvent({
|
|
981
1071
|
eventType: "response",
|
|
@@ -988,13 +1078,14 @@ function register(api) {
|
|
|
988
1078
|
textPreview: responseText.slice(0, 200),
|
|
989
1079
|
textLength,
|
|
990
1080
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
991
|
-
}, config, pluginFilePath);
|
|
1081
|
+
}, config, pluginFilePath, HOOK_LLM_OUTPUT);
|
|
992
1082
|
});
|
|
993
1083
|
api.on("agent_end", async (event, ctx) => {
|
|
994
|
-
const conversationId = resolveConversationId();
|
|
995
1084
|
if (config.debug) {
|
|
996
|
-
|
|
1085
|
+
debugLogRaw({ event, ctx }, HOOK_AGENT_END);
|
|
1086
|
+
debugLog(`Agent ended: success=${event.success}${event.durationMs ? `, duration=${event.durationMs}ms` : ""}${event.error ? `, error=${event.error}` : ""}`, HOOK_AGENT_END);
|
|
997
1087
|
}
|
|
1088
|
+
const conversationId = resolveConversationId();
|
|
998
1089
|
void sendEvent({
|
|
999
1090
|
eventType: "stop",
|
|
1000
1091
|
agent: config.agentName,
|
|
@@ -1005,13 +1096,14 @@ function register(api) {
|
|
|
1005
1096
|
durationMs: event.durationMs,
|
|
1006
1097
|
response: event.error || void 0,
|
|
1007
1098
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1008
|
-
}, config, pluginFilePath);
|
|
1099
|
+
}, config, pluginFilePath, HOOK_AGENT_END);
|
|
1009
1100
|
});
|
|
1010
1101
|
api.on("before_compaction", async (event, ctx) => {
|
|
1011
|
-
const conversationId = resolveConversationId();
|
|
1012
1102
|
if (config.debug) {
|
|
1013
|
-
|
|
1103
|
+
debugLogRaw({ event, ctx }, HOOK_BEFORE_COMPACTION);
|
|
1104
|
+
debugLog(`Context compaction: ${event.messageCount} messages${event.tokenCount ? `, ${event.tokenCount} tokens` : ""}`, HOOK_BEFORE_COMPACTION);
|
|
1014
1105
|
}
|
|
1106
|
+
const conversationId = resolveConversationId();
|
|
1015
1107
|
void sendEvent({
|
|
1016
1108
|
eventType: "context_compact",
|
|
1017
1109
|
agent: config.agentName,
|
|
@@ -1020,13 +1112,14 @@ function register(api) {
|
|
|
1020
1112
|
conversationId,
|
|
1021
1113
|
trigger: `${event.messageCount} messages${event.tokenCount ? `, ${event.tokenCount} tokens` : ""}`,
|
|
1022
1114
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1023
|
-
}, config, pluginFilePath);
|
|
1115
|
+
}, config, pluginFilePath, HOOK_BEFORE_COMPACTION);
|
|
1024
1116
|
});
|
|
1025
1117
|
api.on("subagent_spawned", async (event, ctx) => {
|
|
1026
|
-
const conversationId = resolveConversationId();
|
|
1027
1118
|
if (config.debug) {
|
|
1028
|
-
|
|
1119
|
+
debugLogRaw({ event, ctx }, HOOK_SUBAGENT_SPAWNED);
|
|
1120
|
+
debugLog(`Subagent spawned: ${event.agentId} (${event.mode}${event.label ? `, label=${event.label}` : ""})`, HOOK_SUBAGENT_SPAWNED);
|
|
1029
1121
|
}
|
|
1122
|
+
const conversationId = resolveConversationId();
|
|
1030
1123
|
void sendEvent({
|
|
1031
1124
|
eventType: "subagent_start",
|
|
1032
1125
|
agent: config.agentName,
|
|
@@ -1037,13 +1130,14 @@ function register(api) {
|
|
|
1037
1130
|
subagentMode: event.mode,
|
|
1038
1131
|
subagentLabel: event.label,
|
|
1039
1132
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1040
|
-
}, config, pluginFilePath);
|
|
1133
|
+
}, config, pluginFilePath, HOOK_SUBAGENT_SPAWNED);
|
|
1041
1134
|
});
|
|
1042
1135
|
api.on("subagent_ended", async (event, ctx) => {
|
|
1043
|
-
const conversationId = resolveConversationId();
|
|
1044
1136
|
if (config.debug) {
|
|
1045
|
-
|
|
1137
|
+
debugLogRaw({ event, ctx }, HOOK_SUBAGENT_ENDED);
|
|
1138
|
+
debugLog(`Subagent ended: ${event.targetKind} reason=${event.reason}${event.outcome ? `, outcome=${event.outcome}` : ""}`, HOOK_SUBAGENT_ENDED);
|
|
1046
1139
|
}
|
|
1140
|
+
const conversationId = resolveConversationId();
|
|
1047
1141
|
void sendEvent({
|
|
1048
1142
|
eventType: "subagent_stop",
|
|
1049
1143
|
agent: config.agentName,
|
|
@@ -1054,7 +1148,7 @@ function register(api) {
|
|
|
1054
1148
|
subagentType: event.targetKind,
|
|
1055
1149
|
response: event.error || void 0,
|
|
1056
1150
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1057
|
-
}, config, pluginFilePath);
|
|
1151
|
+
}, config, pluginFilePath, HOOK_SUBAGENT_ENDED);
|
|
1058
1152
|
});
|
|
1059
1153
|
api.registerHook(
|
|
1060
1154
|
["command:new", "command:stop", "command:reset"],
|
|
@@ -1065,7 +1159,8 @@ function register(api) {
|
|
|
1065
1159
|
reset: "session_start"
|
|
1066
1160
|
};
|
|
1067
1161
|
if (config.debug) {
|
|
1068
|
-
|
|
1162
|
+
debugLogRaw(event, HOOK_COMMAND);
|
|
1163
|
+
debugLog(`Command event: ${event.action}`, HOOK_COMMAND);
|
|
1069
1164
|
}
|
|
1070
1165
|
void sendEvent({
|
|
1071
1166
|
toolName: `command:${event.action}`,
|
|
@@ -1076,7 +1171,7 @@ function register(api) {
|
|
|
1076
1171
|
sessionId: resolveConversationId(),
|
|
1077
1172
|
conversationId: resolveConversationId(),
|
|
1078
1173
|
timestamp: event.timestamp.toISOString()
|
|
1079
|
-
}, config, pluginFilePath);
|
|
1174
|
+
}, config, pluginFilePath, HOOK_COMMAND);
|
|
1080
1175
|
},
|
|
1081
1176
|
{ name: "agentapprove-command-monitor", description: "Log command events to Agent Approve" }
|
|
1082
1177
|
);
|
|
@@ -1088,7 +1183,8 @@ function register(api) {
|
|
|
1088
1183
|
const peer = isInbound ? event.context.from : event.context.to;
|
|
1089
1184
|
const channelLabel = [provider, peer].filter(Boolean).join(":") || void 0;
|
|
1090
1185
|
if (config.debug) {
|
|
1091
|
-
|
|
1186
|
+
debugLogRaw(event, HOOK_MESSAGE);
|
|
1187
|
+
debugLog(`Message event: ${event.action} ${channelLabel || "(no channel)"}`, HOOK_MESSAGE);
|
|
1092
1188
|
}
|
|
1093
1189
|
const payload = {
|
|
1094
1190
|
toolName: `message:${event.action}`,
|
|
@@ -1110,7 +1206,7 @@ function register(api) {
|
|
|
1110
1206
|
payload.prompt = `${peer}: ${content}`;
|
|
1111
1207
|
}
|
|
1112
1208
|
}
|
|
1113
|
-
void sendEvent(payload, config, pluginFilePath);
|
|
1209
|
+
void sendEvent(payload, config, pluginFilePath, HOOK_MESSAGE);
|
|
1114
1210
|
},
|
|
1115
1211
|
{ name: "agentapprove-session-monitor", description: "Log message events to Agent Approve" }
|
|
1116
1212
|
);
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw",
|
|
3
3
|
"name": "Agent Approve",
|
|
4
4
|
"description": "Mobile approval for AI agent tool execution. Approve or deny tool calls from your iPhone and Apple Watch.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.7",
|
|
6
6
|
"homepage": "https://agentapprove.com",
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED