openclaw-safeclaw-plugin 0.8.2 → 0.9.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.
Files changed (3) hide show
  1. package/dist/index.js +32 -3
  2. package/index.ts +38 -3
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -53,6 +53,24 @@ async function post(path, body) {
53
53
  return null; // Caller checks failMode
54
54
  }
55
55
  }
56
+ let handshakeCompleted = false;
57
+ async function performHandshake() {
58
+ if (!config.apiKey) {
59
+ console.warn('[SafeClaw] No API key configured — skipping handshake');
60
+ return false;
61
+ }
62
+ const r = await post('/handshake', {
63
+ pluginVersion: '0.1.3',
64
+ configHash: configHash(config),
65
+ });
66
+ if (r === null) {
67
+ console.warn('[SafeClaw] ✗ Handshake failed — API key may be invalid or service unreachable');
68
+ return false;
69
+ }
70
+ console.log(`[SafeClaw] ✓ Handshake OK — org=${r.orgId}, scope=${r.scope}, engine=${r.engineReady ? 'ready' : 'not ready'}`);
71
+ handshakeCompleted = true;
72
+ return true;
73
+ }
56
74
  async function checkConnection() {
57
75
  const label = `[SafeClaw]`;
58
76
  console.log(`${label} Connecting to ${config.serviceUrl} ...`);
@@ -82,7 +100,7 @@ async function checkConnection() {
82
100
  export default {
83
101
  id: 'safeclaw',
84
102
  name: 'SafeClaw Neurosymbolic Governance',
85
- version: '0.1.2',
103
+ version: '0.1.3',
86
104
  register(api) {
87
105
  if (!config.enabled) {
88
106
  console.log('[SafeClaw] Plugin disabled');
@@ -106,8 +124,16 @@ export default {
106
124
  // Heartbeat failure is non-fatal
107
125
  }
108
126
  };
109
- // Start heartbeat after connection check
110
- checkConnection().then(() => sendHeartbeat()).catch(() => { });
127
+ // Start heartbeat after connection check + handshake
128
+ checkConnection()
129
+ .then(() => performHandshake())
130
+ .then((ok) => {
131
+ if (!ok && config.failMode === 'closed') {
132
+ console.warn('[SafeClaw] ⚠ Handshake failed with fail-mode=closed — tool calls will be BLOCKED');
133
+ }
134
+ return sendHeartbeat();
135
+ })
136
+ .catch(() => { });
111
137
  const heartbeatInterval = setInterval(sendHeartbeat, 30000);
112
138
  // Clean shutdown: send shutdown heartbeat and clear interval
113
139
  const shutdown = () => {
@@ -127,6 +153,9 @@ export default {
127
153
  process.on('SIGTERM', () => { shutdown(); process.exit(0); });
128
154
  // THE GATE — constraint checking on every tool call
129
155
  api.on('before_tool_call', async (event, ctx) => {
156
+ if (!handshakeCompleted && config.failMode === 'closed' && config.enforcement === 'enforce') {
157
+ return { block: true, blockReason: 'SafeClaw handshake not completed (fail-closed)' };
158
+ }
130
159
  const r = await post('/evaluate/tool-call', {
131
160
  sessionId: ctx.sessionId ?? event.sessionId,
132
161
  userId: ctx.userId ?? event.userId,
package/index.ts CHANGED
@@ -79,6 +79,29 @@ interface PluginApi {
79
79
  ): void;
80
80
  }
81
81
 
82
+ let handshakeCompleted = false;
83
+
84
+ async function performHandshake(): Promise<boolean> {
85
+ if (!config.apiKey) {
86
+ console.warn('[SafeClaw] No API key configured — skipping handshake');
87
+ return false;
88
+ }
89
+
90
+ const r = await post('/handshake', {
91
+ pluginVersion: '0.1.3',
92
+ configHash: configHash(config),
93
+ });
94
+
95
+ if (r === null) {
96
+ console.warn('[SafeClaw] ✗ Handshake failed — API key may be invalid or service unreachable');
97
+ return false;
98
+ }
99
+
100
+ console.log(`[SafeClaw] ✓ Handshake OK — org=${r.orgId}, scope=${r.scope}, engine=${r.engineReady ? 'ready' : 'not ready'}`);
101
+ handshakeCompleted = true;
102
+ return true;
103
+ }
104
+
82
105
  async function checkConnection(): Promise<void> {
83
106
  const label = `[SafeClaw]`;
84
107
  console.log(`${label} Connecting to ${config.serviceUrl} ...`);
@@ -107,7 +130,7 @@ async function checkConnection(): Promise<void> {
107
130
  export default {
108
131
  id: 'safeclaw',
109
132
  name: 'SafeClaw Neurosymbolic Governance',
110
- version: '0.1.2',
133
+ version: '0.1.3',
111
134
 
112
135
  register(api: PluginApi) {
113
136
  if (!config.enabled) {
@@ -133,8 +156,16 @@ export default {
133
156
  }
134
157
  };
135
158
 
136
- // Start heartbeat after connection check
137
- checkConnection().then(() => sendHeartbeat()).catch(() => {});
159
+ // Start heartbeat after connection check + handshake
160
+ checkConnection()
161
+ .then(() => performHandshake())
162
+ .then((ok) => {
163
+ if (!ok && config.failMode === 'closed') {
164
+ console.warn('[SafeClaw] ⚠ Handshake failed with fail-mode=closed — tool calls will be BLOCKED');
165
+ }
166
+ return sendHeartbeat();
167
+ })
168
+ .catch(() => {});
138
169
  const heartbeatInterval = setInterval(sendHeartbeat, 30000);
139
170
 
140
171
  // Clean shutdown: send shutdown heartbeat and clear interval
@@ -156,6 +187,10 @@ export default {
156
187
 
157
188
  // THE GATE — constraint checking on every tool call
158
189
  api.on('before_tool_call', async (event: PluginEvent, ctx: PluginContext) => {
190
+ if (!handshakeCompleted && config.failMode === 'closed' && config.enforcement === 'enforce') {
191
+ return { block: true, blockReason: 'SafeClaw handshake not completed (fail-closed)' };
192
+ }
193
+
159
194
  const r = await post('/evaluate/tool-call', {
160
195
  sessionId: ctx.sessionId ?? event.sessionId,
161
196
  userId: ctx.userId ?? event.userId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-safeclaw-plugin",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "SafeClaw Neurosymbolic Governance plugin for OpenClaw — validates AI agent actions against OWL ontologies and SHACL constraints",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",