@goplus/agentguard 1.1.4 → 1.1.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 (53) hide show
  1. package/README.md +20 -2
  2. package/dist/action/index.d.ts.map +1 -1
  3. package/dist/action/index.js +3 -1
  4. package/dist/action/index.js.map +1 -1
  5. package/dist/adapters/hermes.d.ts +25 -0
  6. package/dist/adapters/hermes.d.ts.map +1 -0
  7. package/dist/adapters/hermes.js +131 -0
  8. package/dist/adapters/hermes.js.map +1 -0
  9. package/dist/adapters/index.d.ts +1 -0
  10. package/dist/adapters/index.d.ts.map +1 -1
  11. package/dist/adapters/index.js +3 -1
  12. package/dist/adapters/index.js.map +1 -1
  13. package/dist/adapters/openclaw-plugin.d.ts +12 -1
  14. package/dist/adapters/openclaw-plugin.d.ts.map +1 -1
  15. package/dist/adapters/openclaw-plugin.js +165 -9
  16. package/dist/adapters/openclaw-plugin.js.map +1 -1
  17. package/dist/cli.js +85 -12
  18. package/dist/cli.js.map +1 -1
  19. package/dist/feed/cron.d.ts +25 -0
  20. package/dist/feed/cron.d.ts.map +1 -0
  21. package/dist/feed/cron.js +173 -0
  22. package/dist/feed/cron.js.map +1 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/installers.js +0 -1
  28. package/dist/installers.js.map +1 -1
  29. package/dist/runtime/protect.d.ts.map +1 -1
  30. package/dist/runtime/protect.js +6 -1
  31. package/dist/runtime/protect.js.map +1 -1
  32. package/dist/tests/adapter.test.js +146 -0
  33. package/dist/tests/adapter.test.js.map +1 -1
  34. package/dist/tests/feed-cron.test.d.ts +2 -0
  35. package/dist/tests/feed-cron.test.d.ts.map +1 -0
  36. package/dist/tests/feed-cron.test.js +78 -0
  37. package/dist/tests/feed-cron.test.js.map +1 -0
  38. package/dist/tests/installer.test.js +3 -1
  39. package/dist/tests/installer.test.js.map +1 -1
  40. package/dist/tests/integration.test.js +211 -1
  41. package/dist/tests/integration.test.js.map +1 -1
  42. package/dist/tests/runtime-cloud.test.js +24 -0
  43. package/dist/tests/runtime-cloud.test.js.map +1 -1
  44. package/dist/tests/smoke.test.js +141 -7
  45. package/dist/tests/smoke.test.js.map +1 -1
  46. package/docs/hermes.md +70 -0
  47. package/package.json +1 -1
  48. package/skills/agentguard/README.md +12 -0
  49. package/skills/agentguard/SKILL.md +104 -3
  50. package/skills/agentguard/hermes-hooks.yaml +31 -0
  51. package/skills/agentguard/package.json +1 -1
  52. package/skills/agentguard/scripts/auto-scan.js +3 -2
  53. package/skills/agentguard/scripts/hermes-hook.js +201 -0
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * GoPlus AgentGuard Hermes shell hook.
5
+ *
6
+ * Hermes shell hooks read JSON from stdin and use stdout JSON to influence
7
+ * behavior. For pre_tool_call, returning { action: "block", message: "..." }
8
+ * vetoes tool execution. There is no native "ask" decision in Hermes'
9
+ * pre_tool_call contract, so AgentGuard's ask decision is represented as a
10
+ * block with a confirmation-oriented message.
11
+ */
12
+
13
+ import { join } from 'node:path';
14
+
15
+ function isPostHook(input) {
16
+ const event = typeof input?.hook_event_name === 'string' ? input.hook_event_name : '';
17
+ return event.startsWith('post');
18
+ }
19
+
20
+ function isPreHook(input) {
21
+ return !isPostHook(input);
22
+ }
23
+
24
+ function toolNameFrom(input) {
25
+ return typeof input?.tool_name === 'string' ? input.tool_name : '';
26
+ }
27
+
28
+ function toolInputFrom(input) {
29
+ const toolInput = input?.tool_input ?? input?.args;
30
+ return toolInput && typeof toolInput === 'object' && !Array.isArray(toolInput)
31
+ ? toolInput
32
+ : {};
33
+ }
34
+
35
+ function firstString(...values) {
36
+ for (const value of values) {
37
+ if (typeof value === 'string' && value.length > 0) return value;
38
+ }
39
+ return '';
40
+ }
41
+
42
+ function validatePreToolPayload(input) {
43
+ const toolName = toolNameFrom(input);
44
+ const toolInput = toolInputFrom(input);
45
+
46
+ switch (toolName) {
47
+ case 'terminal':
48
+ if (!firstString(toolInput.command)) return 'Hermes terminal hook payload is missing command';
49
+ return null;
50
+ case 'execute_code':
51
+ if (!firstString(toolInput.code, toolInput.command)) return 'Hermes execute_code hook payload is missing code';
52
+ return null;
53
+ case 'write_file':
54
+ case 'patch':
55
+ case 'read_file':
56
+ if (!firstString(toolInput.path, toolInput.file_path)) return `Hermes ${toolName} hook payload is missing path`;
57
+ return null;
58
+ case 'skill_manage':
59
+ if (!firstString(toolInput.path, toolInput.file_path, toolInput.target, toolInput.skill_path)) {
60
+ return 'Hermes skill_manage hook payload is missing target path';
61
+ }
62
+ return null;
63
+ case 'web_extract':
64
+ case 'browser_navigate':
65
+ if (!firstString(toolInput.url, toolInput.href, toolInput.target)) return `Hermes ${toolName} hook payload is missing URL`;
66
+ return null;
67
+ case 'web_search':
68
+ if (!firstString(toolInput.query, toolInput.url)) return 'Hermes web_search hook payload is missing query';
69
+ return null;
70
+ default:
71
+ return `Hermes tool "${toolName || '(missing)'}" is not recognized by AgentGuard`;
72
+ }
73
+ }
74
+
75
+ function shouldFailClosed(input) {
76
+ return !input || isPreHook(input);
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Load AgentGuard engine + Hermes adapter
81
+ // ---------------------------------------------------------------------------
82
+
83
+ const agentguardPath = join(import.meta.url.replace('file://', ''), '..', '..', '..', '..', 'dist', 'index.js');
84
+
85
+ let createAgentGuard, HermesAdapter, evaluateHook, loadConfig;
86
+
87
+ async function loadEngine() {
88
+ if (process.env.AGENTGUARD_TEST_FORCE_ENGINE_LOAD_FAILURE === '1') {
89
+ return null;
90
+ }
91
+
92
+ try {
93
+ const gs = await import(agentguardPath);
94
+ return {
95
+ createAgentGuard: gs.createAgentGuard || gs.default,
96
+ HermesAdapter: gs.HermesAdapter,
97
+ evaluateHook: gs.evaluateHook,
98
+ loadConfig: gs.loadConfig,
99
+ };
100
+ } catch {
101
+ try {
102
+ const gs = await import('@goplus/agentguard');
103
+ return {
104
+ createAgentGuard: gs.createAgentGuard || gs.default,
105
+ HermesAdapter: gs.HermesAdapter,
106
+ evaluateHook: gs.evaluateHook,
107
+ loadConfig: gs.loadConfig,
108
+ };
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // Read stdin
117
+ // ---------------------------------------------------------------------------
118
+
119
+ function readStdin() {
120
+ return new Promise((resolve) => {
121
+ let data = '';
122
+ let settled = false;
123
+ const finish = (value) => {
124
+ if (settled) return;
125
+ settled = true;
126
+ clearTimeout(timer);
127
+ resolve(value);
128
+ };
129
+ const timer = setTimeout(() => finish(null), 5000);
130
+
131
+ process.stdin.setEncoding('utf-8');
132
+ process.stdin.on('data', (chunk) => (data += chunk));
133
+ process.stdin.on('end', () => {
134
+ try {
135
+ finish(JSON.parse(data));
136
+ } catch {
137
+ finish(null);
138
+ }
139
+ });
140
+ process.stdin.on('error', () => finish(null));
141
+ });
142
+ }
143
+
144
+ // ---------------------------------------------------------------------------
145
+ // Hermes output helpers
146
+ // ---------------------------------------------------------------------------
147
+
148
+ function outputBlock(reason) {
149
+ console.log(JSON.stringify({
150
+ action: 'block',
151
+ message: reason || 'GoPlus AgentGuard blocked this action',
152
+ }));
153
+ process.exit(0);
154
+ }
155
+
156
+ function outputAllow() {
157
+ console.log('{}');
158
+ process.exit(0);
159
+ }
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Main
163
+ // ---------------------------------------------------------------------------
164
+
165
+ async function main() {
166
+ const input = await readStdin();
167
+ if (!input) {
168
+ outputBlock('GoPlus AgentGuard: invalid or missing Hermes hook payload');
169
+ }
170
+
171
+ const validationError = isPreHook(input) ? validatePreToolPayload(input) : null;
172
+ if (validationError) {
173
+ outputBlock(`GoPlus AgentGuard: ${validationError}`);
174
+ }
175
+
176
+ const engine = await loadEngine();
177
+ if (!engine) {
178
+ if (shouldFailClosed(input)) {
179
+ outputBlock('GoPlus AgentGuard: unable to load Hermes hook engine; blocking fail-closed');
180
+ }
181
+ outputAllow();
182
+ }
183
+
184
+ ({ createAgentGuard, HermesAdapter, evaluateHook, loadConfig } = engine);
185
+
186
+ const adapter = new HermesAdapter();
187
+ const config = loadConfig();
188
+ const agentguard = createAgentGuard();
189
+
190
+ const result = await evaluateHook(adapter, input, { config, agentguard });
191
+
192
+ if (result.decision === 'deny') {
193
+ outputBlock(result.reason || 'GoPlus AgentGuard blocked this Hermes tool call');
194
+ } else if (result.decision === 'ask') {
195
+ outputBlock(result.reason || 'GoPlus AgentGuard requires confirmation for this Hermes tool call');
196
+ } else {
197
+ outputAllow();
198
+ }
199
+ }
200
+
201
+ main();